/libraries/fabrik/vendor/aws/aws-sdk-php/src/S3/S3EndpointMiddleware.php

https://github.com/trob/fabrik · PHP · 337 lines · 278 code · 30 blank · 29 comment · 30 complexity · 70327dd87761afc5ff3ddde378fb8506 MD5 · raw file

  1. <?php
  2. namespace Aws\S3;
  3. use Aws\Arn\ArnParser;
  4. use Aws\Arn\ObjectLambdaAccessPointArn;
  5. use Aws\CommandInterface;
  6. use Aws\Endpoint\EndpointProvider;
  7. use Aws\Endpoint\PartitionEndpointProvider;
  8. use GuzzleHttp\Exception\InvalidArgumentException;
  9. use GuzzleHttp\Psr7\Uri;
  10. use Psr\Http\Message\RequestInterface;
  11. /**
  12. * Used to update the URL used for S3 requests to support:
  13. * S3 Accelerate, S3 DualStack or Both. It will build to
  14. * host style paths unless specified, including for S3
  15. * DualStack.
  16. *
  17. * IMPORTANT: this middleware must be added after the "build" step.
  18. *
  19. * @internal
  20. */
  21. class S3EndpointMiddleware
  22. {
  23. private static $exclusions = [
  24. 'CreateBucket' => true,
  25. 'DeleteBucket' => true,
  26. 'ListBuckets' => true,
  27. ];
  28. const NO_PATTERN = 0;
  29. const DUALSTACK = 1;
  30. const ACCELERATE = 2;
  31. const ACCELERATE_DUALSTACK = 3;
  32. const PATH_STYLE = 4;
  33. const HOST_STYLE = 5;
  34. /** @var bool */
  35. private $accelerateByDefault;
  36. /** @var bool */
  37. private $dualStackByDefault;
  38. /** @var bool */
  39. private $pathStyleByDefault;
  40. /** @var string */
  41. private $region;
  42. /** @var callable */
  43. private $endpointProvider;
  44. /** @var callable */
  45. private $nextHandler;
  46. /** @var string */
  47. private $endpoint;
  48. /**
  49. * Create a middleware wrapper function
  50. *
  51. * @param string $region
  52. * @param EndpointProvider $endpointProvider
  53. * @param array $options
  54. *
  55. * @return callable
  56. */
  57. public static function wrap($region, $endpointProvider, array $options)
  58. {
  59. return function (callable $handler) use ($region, $endpointProvider, $options) {
  60. return new self($handler, $region, $options, $endpointProvider);
  61. };
  62. }
  63. public function __construct(
  64. callable $nextHandler,
  65. $region,
  66. array $options,
  67. $endpointProvider = null
  68. ) {
  69. $this->pathStyleByDefault = isset($options['path_style'])
  70. ? (bool) $options['path_style'] : false;
  71. $this->dualStackByDefault = isset($options['dual_stack'])
  72. ? (bool) $options['dual_stack'] : false;
  73. $this->accelerateByDefault = isset($options['accelerate'])
  74. ? (bool) $options['accelerate'] : false;
  75. $this->region = (string) $region;
  76. $this->endpoint = isset($options['endpoint'])
  77. ? $options['endpoint'] : "";
  78. $this->endpointProvider = is_null($endpointProvider)
  79. ? PartitionEndpointProvider::defaultProvider()
  80. : $endpointProvider;
  81. $this->nextHandler = $nextHandler;
  82. }
  83. public function __invoke(CommandInterface $command, RequestInterface $request)
  84. {
  85. if (!empty($this->endpoint)) {
  86. $request = $this->applyEndpoint($command, $request);
  87. } else {
  88. switch ($this->endpointPatternDecider($command, $request)) {
  89. case self::HOST_STYLE:
  90. $request = $this->applyHostStyleEndpoint($command, $request);
  91. break;
  92. case self::NO_PATTERN:
  93. break;
  94. case self::PATH_STYLE:
  95. $request = $this->applyPathStyleEndpointCustomizations($command, $request);
  96. break;
  97. case self::DUALSTACK:
  98. $request = $this->applyDualStackEndpoint($command, $request);
  99. break;
  100. case self::ACCELERATE:
  101. $request = $this->applyAccelerateEndpoint(
  102. $command,
  103. $request,
  104. 's3-accelerate'
  105. );
  106. break;
  107. case self::ACCELERATE_DUALSTACK:
  108. $request = $this->applyAccelerateEndpoint(
  109. $command,
  110. $request,
  111. 's3-accelerate.dualstack'
  112. );
  113. break;
  114. }
  115. }
  116. $nextHandler = $this->nextHandler;
  117. return $nextHandler($command, $request);
  118. }
  119. private static function isRequestHostStyleCompatible(
  120. CommandInterface $command,
  121. RequestInterface $request
  122. ) {
  123. return S3Client::isBucketDnsCompatible($command['Bucket'])
  124. && (
  125. $request->getUri()->getScheme() === 'http'
  126. || strpos($command['Bucket'], '.') === false
  127. )
  128. && filter_var($request->getUri()->getHost(), FILTER_VALIDATE_IP) === false;
  129. }
  130. private function endpointPatternDecider(
  131. CommandInterface $command,
  132. RequestInterface $request
  133. ) {
  134. $accelerate = isset($command['@use_accelerate_endpoint'])
  135. ? $command['@use_accelerate_endpoint'] : $this->accelerateByDefault;
  136. $dualStack = isset($command['@use_dual_stack_endpoint'])
  137. ? $command['@use_dual_stack_endpoint'] : $this->dualStackByDefault;
  138. $pathStyle = isset($command['@use_path_style_endpoint'])
  139. ? $command['@use_path_style_endpoint'] : $this->pathStyleByDefault;
  140. if ($accelerate && $dualStack) {
  141. // When try to enable both for operations excluded from s3-accelerate,
  142. // only dualstack endpoints will be enabled.
  143. return $this->canAccelerate($command)
  144. ? self::ACCELERATE_DUALSTACK
  145. : self::DUALSTACK;
  146. }
  147. if ($accelerate && $this->canAccelerate($command)) {
  148. return self::ACCELERATE;
  149. }
  150. if ($dualStack) {
  151. return self::DUALSTACK;
  152. }
  153. if (!$pathStyle
  154. && self::isRequestHostStyleCompatible($command, $request)
  155. ) {
  156. return self::HOST_STYLE;
  157. }
  158. return self::PATH_STYLE;
  159. }
  160. private function canAccelerate(CommandInterface $command)
  161. {
  162. return empty(self::$exclusions[$command->getName()])
  163. && S3Client::isBucketDnsCompatible($command['Bucket']);
  164. }
  165. private function getBucketStyleHost(CommandInterface $command, $host)
  166. {
  167. // For operations on the base host (e.g. ListBuckets)
  168. if (!isset($command['Bucket'])) {
  169. return $host;
  170. }
  171. return "{$command['Bucket']}.{$host}";
  172. }
  173. private function applyHostStyleEndpoint(
  174. CommandInterface $command,
  175. RequestInterface $request
  176. ) {
  177. $uri = $request->getUri();
  178. $request = $request->withUri(
  179. $uri->withHost($this->getBucketStyleHost(
  180. $command,
  181. $uri->getHost()
  182. ))
  183. ->withPath($this->getBucketlessPath(
  184. $uri->getPath(),
  185. $command
  186. ))
  187. );
  188. return $request;
  189. }
  190. private function applyPathStyleEndpointCustomizations(
  191. CommandInterface $command,
  192. RequestInterface $request
  193. ) {
  194. if ($command->getName() == 'WriteGetObjectResponse') {
  195. $region = $this->region;
  196. $dnsSuffix = $this->endpointProvider
  197. ->getPartition($this->region, 's3')
  198. ->getDnsSuffix();
  199. $host = "{$command['RequestRoute']}.s3-object-lambda.{$region}.{$dnsSuffix}";
  200. $uri = $request->getUri();
  201. $request = $request->withUri(
  202. $uri->withHost($host)
  203. ->withPath($this->getBucketlessPath(
  204. $uri->getPath(),
  205. $command
  206. ))
  207. );
  208. }
  209. return $request;
  210. }
  211. private function applyDualStackEndpoint(
  212. CommandInterface $command,
  213. RequestInterface $request
  214. ) {
  215. $request = $request->withUri(
  216. $request->getUri()->withHost($this->getDualStackHost())
  217. );
  218. if (empty($command['@use_path_style_endpoint'])
  219. && !$this->pathStyleByDefault
  220. && self::isRequestHostStyleCompatible($command, $request)
  221. ) {
  222. $request = $this->applyHostStyleEndpoint($command, $request);
  223. }
  224. return $request;
  225. }
  226. private function getDualStackHost()
  227. {
  228. $dnsSuffix = $this->endpointProvider
  229. ->getPartition($this->region, 's3')
  230. ->getDnsSuffix();
  231. return "s3.dualstack.{$this->region}.{$dnsSuffix}";
  232. }
  233. private function applyAccelerateEndpoint(
  234. CommandInterface $command,
  235. RequestInterface $request,
  236. $pattern
  237. ) {
  238. $request = $request->withUri(
  239. $request->getUri()
  240. ->withHost($this->getAccelerateHost($command, $pattern))
  241. ->withPath($this->getBucketlessPath(
  242. $request->getUri()->getPath(),
  243. $command
  244. ))
  245. );
  246. return $request;
  247. }
  248. private function getAccelerateHost(CommandInterface $command, $pattern)
  249. {
  250. $dnsSuffix = $this->endpointProvider
  251. ->getPartition($this->region, 's3')
  252. ->getDnsSuffix();
  253. return "{$command['Bucket']}.{$pattern}.{$dnsSuffix}";
  254. }
  255. private function getBucketlessPath($path, CommandInterface $command)
  256. {
  257. $pattern = '/^\\/' . preg_quote($command['Bucket'], '/') . '/';
  258. return preg_replace($pattern, '', $path) ?: '/';
  259. }
  260. private function applyEndpoint(
  261. CommandInterface $command,
  262. RequestInterface $request
  263. ) {
  264. $dualStack = isset($command['@use_dual_stack_endpoint'])
  265. ? $command['@use_dual_stack_endpoint'] : $this->dualStackByDefault;
  266. if (ArnParser::isArn($command['Bucket'])) {
  267. $arn = ArnParser::parse($command['Bucket']);
  268. $outpost = $arn->getService() == 's3-outposts';
  269. if ($outpost && $dualStack) {
  270. throw new InvalidArgumentException("Outposts + dualstack is not supported");
  271. }
  272. if ($arn instanceof ObjectLambdaAccessPointArn) {
  273. return $request;
  274. }
  275. }
  276. if ($dualStack) {
  277. throw new InvalidArgumentException("Custom Endpoint + Dualstack not supported");
  278. }
  279. if ($command->getName() == 'WriteGetObjectResponse') {
  280. $host = "{$command['RequestRoute']}.{$this->endpoint}";
  281. $uri = $request->getUri();
  282. return $request = $request->withUri(
  283. $uri->withHost($host)
  284. ->withPath($this->getBucketlessPath(
  285. $uri->getPath(),
  286. $command
  287. ))
  288. );
  289. }
  290. $host = ($this->pathStyleByDefault) ?
  291. $this->endpoint :
  292. $this->getBucketStyleHost(
  293. $command,
  294. $this->endpoint
  295. );
  296. $uri = $request->getUri();
  297. $scheme = $uri->getScheme();
  298. if(empty($scheme)){
  299. $request = $request->withUri(
  300. $uri->withHost($host)
  301. );
  302. } else {
  303. $request = $request->withUri($uri);
  304. }
  305. return $request;
  306. }
  307. }