PluginProbe
Media Cloud Sync / 1.2.0
Media Cloud Sync v1.2.0
1.4.1 1.4.0 1.3.12 1.3.11 1.3.10 trunk 1.0.0 1.0.1 1.0.2 1.0.3 1.1.0 1.1.1 1.2.0 1.2.10 1.2.11 1.2.12 1.2.13 1.2.2 1.2.3 1.2.4 1.2.5 1.2.6 1.2.7 1.2.8 1.2.9 All 35 releases
media-cloud-sync / includes / sdk / s3 / Aws / S3 / S3EndpointMiddleware.php

S3EndpointMiddleware.php in Media Cloud Sync 1.2.0, at includes/sdk/s3/Aws/S3/S3EndpointMiddleware.php

219 lines 9.9 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2
3 namespace Dudlewebs\WPMCS\s3\Aws\S3;
4
5 use Dudlewebs\WPMCS\s3\Aws\Arn\ArnParser;
6 use Dudlewebs\WPMCS\s3\Aws\Arn\ObjectLambdaAccessPointArn;
7 use Dudlewebs\WPMCS\s3\Aws\ClientResolver;
8 use Dudlewebs\WPMCS\s3\Aws\CommandInterface;
9 use Dudlewebs\WPMCS\s3\Aws\Endpoint\EndpointProvider;
10 use Dudlewebs\WPMCS\s3\Aws\Endpoint\PartitionEndpointProvider;
11 use Dudlewebs\WPMCS\s3\GuzzleHttp\Exception\InvalidArgumentException;
12 use Dudlewebs\WPMCS\s3\GuzzleHttp\Psr7\Uri;
13 use Dudlewebs\WPMCS\s3\Psr\Http\Message\RequestInterface;
14 /**
15 * Used to update the URL used for S3 requests to support:
16 * S3 Accelerate, S3 DualStack or Both. It will build to
17 * host style paths unless specified, including for S3
18 * DualStack.
19 *
20 * IMPORTANT: this middleware must be added after the "build" step.
21 *
22 * @internal
23 */
24 class S3EndpointMiddleware
25 {
26 private static $exclusions = ['CreateBucket' => \true, 'DeleteBucket' => \true, 'ListBuckets' => \true];
27 const NO_PATTERN = 0;
28 const DUALSTACK = 1;
29 const ACCELERATE = 2;
30 const ACCELERATE_DUALSTACK = 3;
31 const PATH_STYLE = 4;
32 const HOST_STYLE = 5;
33 /** @var bool */
34 private $accelerateByDefault;
35 /** @var bool */
36 private $dualStackByDefault;
37 /** @var bool */
38 private $pathStyleByDefault;
39 /** @var string */
40 private $region;
41 /** @var callable */
42 private $endpointProvider;
43 /** @var callable */
44 private $nextHandler;
45 /** @var string */
46 private $endpoint;
47 /**
48 * Create a middleware wrapper function
49 *
50 * @param string $region
51 * @param EndpointProvider $endpointProvider
52 * @param array $options
53 *
54 * @return callable
55 */
56 public static function wrap($region, $endpointProvider, array $options)
57 {
58 return function (callable $handler) use($region, $endpointProvider, $options) {
59 return new self($handler, $region, $options, $endpointProvider);
60 };
61 }
62 public function __construct(callable $nextHandler, $region, array $options, $endpointProvider = null)
63 {
64 $this->pathStyleByDefault = isset($options['path_style']) ? (bool) $options['path_style'] : \false;
65 $this->dualStackByDefault = isset($options['dual_stack']) ? (bool) $options['dual_stack'] : \false;
66 $this->accelerateByDefault = isset($options['accelerate']) ? (bool) $options['accelerate'] : \false;
67 $this->region = (string) $region;
68 $this->endpoint = isset($options['endpoint']) ? $options['endpoint'] : "";
69 $this->endpointProvider = \is_null($endpointProvider) ? PartitionEndpointProvider::defaultProvider() : $endpointProvider;
70 $this->nextHandler = $nextHandler;
71 }
72 public function __invoke(CommandInterface $command, RequestInterface $request)
73 {
74 if (!empty($this->endpoint)) {
75 $request = $this->applyEndpoint($command, $request);
76 } else {
77 switch ($this->endpointPatternDecider($command, $request)) {
78 case self::HOST_STYLE:
79 $request = $this->applyHostStyleEndpoint($command, $request);
80 break;
81 case self::NO_PATTERN:
82 break;
83 case self::PATH_STYLE:
84 $request = $this->applyPathStyleEndpointCustomizations($command, $request);
85 break;
86 case self::DUALSTACK:
87 $request = $this->applyDualStackEndpoint($command, $request);
88 break;
89 case self::ACCELERATE:
90 $request = $this->applyAccelerateEndpoint($command, $request, 's3-accelerate');
91 break;
92 case self::ACCELERATE_DUALSTACK:
93 $request = $this->applyAccelerateEndpoint($command, $request, 's3-accelerate.dualstack');
94 break;
95 }
96 }
97 $nextHandler = $this->nextHandler;
98 return $nextHandler($command, $request);
99 }
100 private static function isRequestHostStyleCompatible(CommandInterface $command, RequestInterface $request)
101 {
102 return S3Client::isBucketDnsCompatible($command['Bucket']) && ($request->getUri()->getScheme() === 'http' || \strpos($command['Bucket'], '.') === \false) && \filter_var($request->getUri()->getHost(), \FILTER_VALIDATE_IP) === \false;
103 }
104 private function endpointPatternDecider(CommandInterface $command, RequestInterface $request)
105 {
106 $accelerate = isset($command['@use_accelerate_endpoint']) ? $command['@use_accelerate_endpoint'] : $this->accelerateByDefault;
107 $dualStack = isset($command['@use_dual_stack_endpoint']) ? $command['@use_dual_stack_endpoint'] : $this->dualStackByDefault;
108 $pathStyle = isset($command['@use_path_style_endpoint']) ? $command['@use_path_style_endpoint'] : $this->pathStyleByDefault;
109 if ($accelerate && $dualStack) {
110 // When try to enable both for operations excluded from s3-accelerate,
111 // only dualstack endpoints will be enabled.
112 return $this->canAccelerate($command) ? self::ACCELERATE_DUALSTACK : self::DUALSTACK;
113 }
114 if ($accelerate && $this->canAccelerate($command)) {
115 return self::ACCELERATE;
116 }
117 if ($dualStack) {
118 return self::DUALSTACK;
119 }
120 if (!$pathStyle && self::isRequestHostStyleCompatible($command, $request)) {
121 return self::HOST_STYLE;
122 }
123 return self::PATH_STYLE;
124 }
125 private function canAccelerate(CommandInterface $command)
126 {
127 return empty(self::$exclusions[$command->getName()]) && S3Client::isBucketDnsCompatible($command['Bucket']);
128 }
129 private function getBucketStyleHost(CommandInterface $command, $host)
130 {
131 // For operations on the base host (e.g. ListBuckets)
132 if (!isset($command['Bucket'])) {
133 return $host;
134 }
135 return "{$command['Bucket']}.{$host}";
136 }
137 private function applyHostStyleEndpoint(CommandInterface $command, RequestInterface $request)
138 {
139 $uri = $request->getUri();
140 $request = $request->withUri($uri->withHost($this->getBucketStyleHost($command, $uri->getHost()))->withPath($this->getBucketlessPath($uri->getPath(), $command)));
141 return $request;
142 }
143 private function applyPathStyleEndpointCustomizations(CommandInterface $command, RequestInterface $request)
144 {
145 if ($command->getName() == 'WriteGetObjectResponse') {
146 $dnsSuffix = $this->endpointProvider->getPartition($this->region, 's3')->getDnsSuffix();
147 $fips = \Dudlewebs\WPMCS\s3\Aws\is_fips_pseudo_region($this->region) ? "-fips" : "";
148 $region = \Dudlewebs\WPMCS\s3\Aws\strip_fips_pseudo_regions($this->region);
149 $host = "{$command['RequestRoute']}.s3-object-lambda{$fips}.{$region}.{$dnsSuffix}";
150 $uri = $request->getUri();
151 $request = $request->withUri($uri->withHost($host)->withPath($this->getBucketlessPath($uri->getPath(), $command)));
152 }
153 return $request;
154 }
155 private function applyDualStackEndpoint(CommandInterface $command, RequestInterface $request)
156 {
157 $request = $request->withUri($request->getUri()->withHost($this->getDualStackHost()));
158 if (empty($command['@use_path_style_endpoint']) && !$this->pathStyleByDefault && self::isRequestHostStyleCompatible($command, $request)) {
159 $request = $this->applyHostStyleEndpoint($command, $request);
160 }
161 return $request;
162 }
163 private function getDualStackHost()
164 {
165 $dnsSuffix = $this->endpointProvider->getPartition($this->region, 's3')->getDnsSuffix();
166 return "s3.dualstack.{$this->region}.{$dnsSuffix}";
167 }
168 private function applyAccelerateEndpoint(CommandInterface $command, RequestInterface $request, $pattern)
169 {
170 $request = $request->withUri($request->getUri()->withHost($this->getAccelerateHost($command, $pattern))->withPath($this->getBucketlessPath($request->getUri()->getPath(), $command)));
171 return $request;
172 }
173 private function getAccelerateHost(CommandInterface $command, $pattern)
174 {
175 $dnsSuffix = $this->endpointProvider->getPartition($this->region, 's3')->getDnsSuffix();
176 return "{$command['Bucket']}.{$pattern}.{$dnsSuffix}";
177 }
178 private function getBucketlessPath($path, CommandInterface $command)
179 {
180 $pattern = '/^\\/' . \preg_quote($command['Bucket'], '/') . '/';
181 $path = \preg_replace($pattern, '', $path) ?: '/';
182 if (\substr($path, 0, 1) !== '/') {
183 $path = '/' . $path;
184 }
185 return $path;
186 }
187 private function applyEndpoint(CommandInterface $command, RequestInterface $request)
188 {
189 $dualStack = isset($command['@use_dual_stack_endpoint']) ? $command['@use_dual_stack_endpoint'] : $this->dualStackByDefault;
190 if (ArnParser::isArn($command['Bucket'])) {
191 $arn = ArnParser::parse($command['Bucket']);
192 $outpost = $arn->getService() == 's3-outposts';
193 if ($outpost && $dualStack) {
194 throw new InvalidArgumentException("Outposts + dualstack is not supported");
195 }
196 if ($arn instanceof ObjectLambdaAccessPointArn) {
197 return $request;
198 }
199 }
200 if ($dualStack) {
201 throw new InvalidArgumentException("Custom Endpoint + Dualstack not supported");
202 }
203 if ($command->getName() == 'WriteGetObjectResponse') {
204 $host = "{$command['RequestRoute']}.{$this->endpoint}";
205 $uri = $request->getUri();
206 return $request = $request->withUri($uri->withHost($host)->withPath($this->getBucketlessPath($uri->getPath(), $command)));
207 }
208 $host = $this->pathStyleByDefault ? $this->endpoint : $this->getBucketStyleHost($command, $this->endpoint);
209 $uri = $request->getUri();
210 $scheme = $uri->getScheme();
211 if (empty($scheme)) {
212 $request = $request->withUri($uri->withHost($host));
213 } else {
214 $request = $request->withUri($uri);
215 }
216 return $request;
217 }
218 }
219