AbstractClient.php 9.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272
  1. <?php
  2. /**
  3. * Copyright 2010-2013 Amazon.com, Inc. or its affiliates. All Rights Reserved.
  4. *
  5. * Licensed under the Apache License, Version 2.0 (the "License").
  6. * You may not use this file except in compliance with the License.
  7. * A copy of the License is located at
  8. *
  9. * http://aws.amazon.com/apache2.0
  10. *
  11. * or in the "license" file accompanying this file. This file is distributed
  12. * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
  13. * express or implied. See the License for the specific language governing
  14. * permissions and limitations under the License.
  15. */
  16. namespace Aws\Common\Client;
  17. use Aws\Common\Aws;
  18. use Aws\Common\Credentials\CredentialsInterface;
  19. use Aws\Common\Credentials\NullCredentials;
  20. use Aws\Common\Enum\ClientOptions as Options;
  21. use Aws\Common\Exception\InvalidArgumentException;
  22. use Aws\Common\Exception\TransferException;
  23. use Aws\Common\Signature\EndpointSignatureInterface;
  24. use Aws\Common\Signature\SignatureInterface;
  25. use Aws\Common\Signature\SignatureListener;
  26. use Aws\Common\Waiter\WaiterClassFactory;
  27. use Aws\Common\Waiter\CompositeWaiterFactory;
  28. use Aws\Common\Waiter\WaiterFactoryInterface;
  29. use Aws\Common\Waiter\WaiterConfigFactory;
  30. use Guzzle\Common\Collection;
  31. use Guzzle\Http\Exception\CurlException;
  32. use Guzzle\Service\Client;
  33. use Guzzle\Service\Description\ServiceDescriptionInterface;
  34. /**
  35. * Abstract AWS client
  36. */
  37. abstract class AbstractClient extends Client implements AwsClientInterface
  38. {
  39. /**
  40. * @var CredentialsInterface AWS credentials
  41. */
  42. protected $credentials;
  43. /**
  44. * @var SignatureInterface Signature implementation of the service
  45. */
  46. protected $signature;
  47. /**
  48. * @var WaiterFactoryInterface Factory used to create waiter classes
  49. */
  50. protected $waiterFactory;
  51. /**
  52. * {@inheritdoc}
  53. */
  54. public static function getAllEvents()
  55. {
  56. return array_merge(Client::getAllEvents(), array(
  57. 'client.region_changed',
  58. 'client.credentials_changed',
  59. ));
  60. }
  61. /**
  62. * @param CredentialsInterface $credentials AWS credentials
  63. * @param SignatureInterface $signature Signature implementation
  64. * @param Collection $config Configuration options
  65. *
  66. * @throws InvalidArgumentException if an endpoint provider isn't provided
  67. */
  68. public function __construct(CredentialsInterface $credentials, SignatureInterface $signature, Collection $config)
  69. {
  70. // Bootstrap with Guzzle
  71. parent::__construct($config->get(Options::BASE_URL), $config);
  72. $this->credentials = $credentials;
  73. $this->signature = $signature;
  74. // Make sure the user agent is prefixed by the SDK version
  75. $this->setUserAgent('aws-sdk-php2/' . Aws::VERSION, true);
  76. // Add the event listener so that requests are signed before they are sent
  77. $dispatcher = $this->getEventDispatcher();
  78. if (!$credentials instanceof NullCredentials) {
  79. $dispatcher->addSubscriber(new SignatureListener($credentials, $signature));
  80. }
  81. if ($backoff = $config->get(Options::BACKOFF)) {
  82. $dispatcher->addSubscriber($backoff, -255);
  83. }
  84. }
  85. public function __call($method, $args)
  86. {
  87. if (substr($method, 0, 3) === 'get' && substr($method, -8) === 'Iterator') {
  88. // Allow magic method calls for iterators (e.g. $client->get<CommandName>Iterator($params))
  89. $commandOptions = isset($args[0]) ? $args[0] : null;
  90. $iteratorOptions = isset($args[1]) ? $args[1] : array();
  91. return $this->getIterator(substr($method, 3, -8), $commandOptions, $iteratorOptions);
  92. } elseif (substr($method, 0, 9) == 'waitUntil') {
  93. // Allow magic method calls for waiters (e.g. $client->waitUntil<WaiterName>($params))
  94. return $this->waitUntil(substr($method, 9), isset($args[0]) ? $args[0]: array());
  95. } else {
  96. return parent::__call(ucfirst($method), $args);
  97. }
  98. }
  99. /**
  100. * Get an endpoint for a specific region from a service description
  101. * @deprecated This function will no longer be updated to work with new regions.
  102. */
  103. public static function getEndpoint(ServiceDescriptionInterface $description, $region, $scheme)
  104. {
  105. $service = $description->getData('serviceFullName');
  106. // Lookup the region in the service description
  107. if (!($regions = $description->getData('regions'))) {
  108. throw new InvalidArgumentException("No regions found in the {$service} description");
  109. }
  110. // Ensure that the region exists for the service
  111. if (!isset($regions[$region])) {
  112. throw new InvalidArgumentException("{$region} is not a valid region for {$service}");
  113. }
  114. // Ensure that the scheme is valid
  115. if ($regions[$region][$scheme] == false) {
  116. throw new InvalidArgumentException("{$scheme} is not a valid URI scheme for {$service} in {$region}");
  117. }
  118. return $scheme . '://' . $regions[$region]['hostname'];
  119. }
  120. public function getCredentials()
  121. {
  122. return $this->credentials;
  123. }
  124. public function setCredentials(CredentialsInterface $credentials)
  125. {
  126. $formerCredentials = $this->credentials;
  127. $this->credentials = $credentials;
  128. // Dispatch an event that the credentials have been changed
  129. $this->dispatch('client.credentials_changed', array(
  130. 'credentials' => $credentials,
  131. 'former_credentials' => $formerCredentials,
  132. ));
  133. return $this;
  134. }
  135. public function getSignature()
  136. {
  137. return $this->signature;
  138. }
  139. public function getRegions()
  140. {
  141. return $this->serviceDescription->getData('regions');
  142. }
  143. public function getRegion()
  144. {
  145. return $this->getConfig(Options::REGION);
  146. }
  147. public function setRegion($region)
  148. {
  149. $config = $this->getConfig();
  150. $formerRegion = $config->get(Options::REGION);
  151. $global = $this->serviceDescription->getData('globalEndpoint');
  152. $provider = $config->get('endpoint_provider');
  153. if (!$provider) {
  154. throw new \RuntimeException('No endpoint provider configured');
  155. }
  156. // Only change the region if the service does not have a global endpoint
  157. if (!$global || $this->serviceDescription->getData('namespace') === 'S3') {
  158. $endpoint = call_user_func(
  159. $provider,
  160. array(
  161. 'scheme' => $config->get(Options::SCHEME),
  162. 'region' => $region,
  163. 'service' => $config->get(Options::SERVICE)
  164. )
  165. );
  166. $this->setBaseUrl($endpoint['endpoint']);
  167. $config->set(Options::BASE_URL, $endpoint['endpoint']);
  168. $config->set(Options::REGION, $region);
  169. // Update the signature if necessary
  170. $signature = $this->getSignature();
  171. if ($signature instanceof EndpointSignatureInterface) {
  172. /** @var $signature EndpointSignatureInterface */
  173. $signature->setRegionName($region);
  174. }
  175. // Dispatch an event that the region has been changed
  176. $this->dispatch('client.region_changed', array(
  177. 'region' => $region,
  178. 'former_region' => $formerRegion,
  179. ));
  180. }
  181. return $this;
  182. }
  183. public function waitUntil($waiter, array $input = array())
  184. {
  185. $this->getWaiter($waiter, $input)->wait();
  186. return $this;
  187. }
  188. public function getWaiter($waiter, array $input = array())
  189. {
  190. return $this->getWaiterFactory()->build($waiter)
  191. ->setClient($this)
  192. ->setConfig($input);
  193. }
  194. public function setWaiterFactory(WaiterFactoryInterface $waiterFactory)
  195. {
  196. $this->waiterFactory = $waiterFactory;
  197. return $this;
  198. }
  199. public function getWaiterFactory()
  200. {
  201. if (!$this->waiterFactory) {
  202. $clientClass = get_class($this);
  203. // Use a composite factory that checks for classes first, then config waiters
  204. $this->waiterFactory = new CompositeWaiterFactory(array(
  205. new WaiterClassFactory(substr($clientClass, 0, strrpos($clientClass, '\\')) . '\\Waiter')
  206. ));
  207. if ($this->getDescription()) {
  208. $waiterConfig = $this->getDescription()->getData('waiters') ?: array();
  209. $this->waiterFactory->addFactory(new WaiterConfigFactory($waiterConfig));
  210. }
  211. }
  212. return $this->waiterFactory;
  213. }
  214. public function getApiVersion()
  215. {
  216. return $this->serviceDescription->getApiVersion();
  217. }
  218. /**
  219. * {@inheritdoc}
  220. * @throws \Aws\Common\Exception\TransferException
  221. */
  222. public function send($requests)
  223. {
  224. try {
  225. return parent::send($requests);
  226. } catch (CurlException $e) {
  227. $wrapped = new TransferException($e->getMessage(), null, $e);
  228. $wrapped->setCurlHandle($e->getCurlHandle())
  229. ->setCurlInfo($e->getCurlInfo())
  230. ->setError($e->getError(), $e->getErrorNo())
  231. ->setRequest($e->getRequest());
  232. throw $wrapped;
  233. }
  234. }
  235. }