Partition.php 5.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183
  1. <?php
  2. namespace Aws\Endpoint;
  3. use ArrayAccess;
  4. use Aws\HasDataTrait;
  5. use InvalidArgumentException as Iae;
  6. /**
  7. * Default implementation of an AWS partition.
  8. */
  9. final class Partition implements ArrayAccess, PartitionInterface
  10. {
  11. use HasDataTrait;
  12. /**
  13. * The partition constructor accepts the following options:
  14. *
  15. * - `partition`: (string, required) The partition name as specified in an
  16. * ARN (e.g., `aws`)
  17. * - `partitionName`: (string) The human readable name of the partition
  18. * (e.g., "AWS Standard")
  19. * - `dnsSuffix`: (string, required) The DNS suffix of the partition. This
  20. * value is used to determine how endpoints in the partition are resolved.
  21. * - `regionRegex`: (string) A PCRE regular expression that specifies the
  22. * pattern that region names in the endpoint adhere to.
  23. * - `regions`: (array, required) A map of the regions in the partition.
  24. * Each key is the region as present in a hostname (e.g., `us-east-1`),
  25. * and each value is a structure containing region information.
  26. * - `defaults`: (array) A map of default key value pairs to apply to each
  27. * endpoint of the partition. Any value in an `endpoint` definition will
  28. * supersede any values specified in `defaults`.
  29. * - `services`: (array, required) A map of service endpoint prefix name
  30. * (the value found in a hostname) to information about the service.
  31. *
  32. * @param array $definition
  33. *
  34. * @throws Iae if any required options are missing
  35. */
  36. public function __construct(array $definition)
  37. {
  38. foreach (['partition', 'regions', 'services', 'dnsSuffix'] as $key) {
  39. if (!isset($definition[$key])) {
  40. throw new Iae("Partition missing required $key field");
  41. }
  42. }
  43. $this->data = $definition;
  44. }
  45. public function getName()
  46. {
  47. return $this->data['partition'];
  48. }
  49. public function isRegionMatch($region, $service)
  50. {
  51. if (isset($this->data['regions'][$region])
  52. || isset($this->data['services'][$service]['endpoints'][$region])
  53. ) {
  54. return true;
  55. }
  56. if (isset($this->data['regionRegex'])) {
  57. return (bool) preg_match(
  58. "@{$this->data['regionRegex']}@",
  59. $region
  60. );
  61. }
  62. return false;
  63. }
  64. public function getAvailableEndpoints(
  65. $service,
  66. $allowNonRegionalEndpoints = false
  67. ) {
  68. if ($this->isServicePartitionGlobal($service)) {
  69. return [$this->getPartitionEndpoint($service)];
  70. }
  71. if (isset($this->data['services'][$service]['endpoints'])) {
  72. $serviceRegions = array_keys(
  73. $this->data['services'][$service]['endpoints']
  74. );
  75. return $allowNonRegionalEndpoints
  76. ? $serviceRegions
  77. : array_intersect($serviceRegions, array_keys(
  78. $this->data['regions']
  79. ));
  80. }
  81. return [];
  82. }
  83. public function __invoke(array $args = [])
  84. {
  85. $service = isset($args['service']) ? $args['service'] : '';
  86. $region = isset($args['region']) ? $args['region'] : '';
  87. $scheme = isset($args['scheme']) ? $args['scheme'] : 'https';
  88. $data = $this->getEndpointData($service, $region);
  89. return [
  90. 'endpoint' => "{$scheme}://" . $this->formatEndpoint(
  91. isset($data['hostname']) ? $data['hostname'] : '',
  92. $service,
  93. $region
  94. ),
  95. 'signatureVersion' => $this->getSignatureVersion($data),
  96. 'signingRegion' => isset($data['credentialScope']['region'])
  97. ? $data['credentialScope']['region']
  98. : $region,
  99. 'signingName' => isset($data['credentialScope']['service'])
  100. ? $data['credentialScope']['service']
  101. : $service,
  102. ];
  103. }
  104. private function getEndpointData($service, $region)
  105. {
  106. $resolved = $this->resolveRegion($service, $region);
  107. $data = isset($this->data['services'][$service]['endpoints'][$resolved])
  108. ? $this->data['services'][$service]['endpoints'][$resolved]
  109. : [];
  110. $data += isset($this->data['services'][$service]['defaults'])
  111. ? $this->data['services'][$service]['defaults']
  112. : [];
  113. $data += isset($this->data['defaults'])
  114. ? $this->data['defaults']
  115. : [];
  116. return $data;
  117. }
  118. private function getSignatureVersion(array $data)
  119. {
  120. static $supportedBySdk = [
  121. 's3v4',
  122. 'v4',
  123. 'anonymous',
  124. ];
  125. $possibilities = array_intersect(
  126. $supportedBySdk,
  127. isset($data['signatureVersions'])
  128. ? $data['signatureVersions']
  129. : ['v4']
  130. );
  131. return array_shift($possibilities);
  132. }
  133. private function resolveRegion($service, $region)
  134. {
  135. if ($this->isServicePartitionGlobal($service)) {
  136. return $this->getPartitionEndpoint($service);
  137. }
  138. return $region;
  139. }
  140. private function isServicePartitionGlobal($service)
  141. {
  142. return isset($this->data['services'][$service]['isRegionalized'])
  143. && false === $this->data['services'][$service]['isRegionalized']
  144. && isset($this->data['services'][$service]['partitionEndpoint']);
  145. }
  146. private function getPartitionEndpoint($service)
  147. {
  148. return $this->data['services'][$service]['partitionEndpoint'];
  149. }
  150. private function formatEndpoint($template, $service, $region)
  151. {
  152. return strtr($template, [
  153. '{service}' => $service,
  154. '{region}' => $region,
  155. '{dnsSuffix}' => $this->data['dnsSuffix'],
  156. ]);
  157. }
  158. }