PluginProbe
Media Cloud Sync / 1.3.11
Media Cloud Sync v1.3.11
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 1.3.0 All 34 releases
media-cloud-sync / includes / sdk / s3 / Aws / MetricsBuilder.php

MetricsBuilder.php in Media Cloud Sync 1.3.11, at includes/sdk/s3/Aws/MetricsBuilder.php

384 lines 14.0 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;
4
5 use Dudlewebs\WPMCS\s3\Aws\Credentials\CredentialsInterface;
6 use Dudlewebs\WPMCS\s3\Aws\Credentials\CredentialSources;
7 use Dudlewebs\WPMCS\s3\Aws\Token;
8 use Dudlewebs\WPMCS\s3\Aws\Token\TokenInterface;
9 /**
10 * A placeholder for gathering metrics in a request.
11 *
12 * @internal
13 */
14 final class MetricsBuilder
15 {
16 const WAITER = "B";
17 const PAGINATOR = "C";
18 const RETRY_MODE_LEGACY = "D";
19 const RETRY_MODE_STANDARD = "E";
20 const RETRY_MODE_ADAPTIVE = "F";
21 const S3_TRANSFER = "G";
22 const S3_CRYPTO_V1N = "H";
23 const S3_CRYPTO_V2 = "I";
24 const S3_EXPRESS_BUCKET = "J";
25 const GZIP_REQUEST_COMPRESSION = "L";
26 const ENDPOINT_OVERRIDE = "N";
27 const ACCOUNT_ID_ENDPOINT = "O";
28 const ACCOUNT_ID_MODE_PREFERRED = "P";
29 const ACCOUNT_ID_MODE_DISABLED = "Q";
30 const ACCOUNT_ID_MODE_REQUIRED = "R";
31 const BEARER_SERVICE_ENV_VARS = "3";
32 const SIGV4A_SIGNING = "S";
33 const RESOLVED_ACCOUNT_ID = "T";
34 const FLEXIBLE_CHECKSUMS_REQ_CRC32 = "U";
35 const FLEXIBLE_CHECKSUMS_REQ_CRC32C = "V";
36 const FLEXIBLE_CHECKSUMS_REQ_CRC64 = "W";
37 const FLEXIBLE_CHECKSUMS_REQ_SHA1 = "X";
38 const FLEXIBLE_CHECKSUMS_REQ_SHA256 = "Y";
39 const FLEXIBLE_CHECKSUMS_REQ_WHEN_SUPPORTED = "Z";
40 const FLEXIBLE_CHECKSUMS_REQ_WHEN_REQUIRED = "a";
41 const FLEXIBLE_CHECKSUMS_RES_WHEN_SUPPORTED = "b";
42 const FLEXIBLE_CHECKSUMS_RES_WHEN_REQUIRED = "c";
43 const CREDENTIALS_CODE = "e";
44 const CREDENTIALS_ENV_VARS = "g";
45 const CREDENTIALS_ENV_VARS_STS_WEB_ID_TOKEN = "h";
46 const CREDENTIALS_STS_ASSUME_ROLE = "i";
47 const CREDENTIALS_STS_ASSUME_ROLE_WEB_ID = "k";
48 const CREDENTIALS_PROFILE = "n";
49 const CREDENTIALS_PROFILE_STS_WEB_ID_TOKEN = "q";
50 const CREDENTIALS_HTTP = "z";
51 const CREDENTIALS_IMDS = "0";
52 const CREDENTIALS_PROFILE_PROCESS = "v";
53 const CREDENTIALS_PROFILE_SSO = "r";
54 const CREDENTIALS_PROFILE_SSO_LEGACY = "t";
55 /** @var int */
56 private static $MAX_METRICS_SIZE = 1024;
57 // 1KB or 1024 B
58 /** @var string */
59 private static $METRIC_SEPARATOR = ",";
60 /** @var array $metrics */
61 private $metrics;
62 /** @var int $metricsSize */
63 private $metricsSize;
64 public function __construct()
65 {
66 $this->metrics = [];
67 // The first metrics does not include the separator
68 // therefore it is reduced by default.
69 $this->metricsSize = -\strlen(self::$METRIC_SEPARATOR);
70 }
71 /**
72 * Build the metrics string value.
73 *
74 * @return string
75 */
76 public function build() : string
77 {
78 if (empty($this->metrics)) {
79 return "";
80 }
81 return $this->encode();
82 }
83 /**
84 * Encodes the metrics by separating each metric
85 * with a comma. Example: for the metrics[A,B,C] then
86 * the output would be "A,B,C".
87 *
88 * @return string
89 */
90 private function encode() : string
91 {
92 return \implode(self::$METRIC_SEPARATOR, \array_keys($this->metrics));
93 }
94 /**
95 * Appends a metric to the internal metrics holder after validating it.
96 * Increases the current metrics size by the length of the new metric
97 * plus the length of the encoding separator.
98 * Example: $currentSize = $currentSize + len($newMetric) + len($separator)
99 *
100 * @param string $metric The metric to append.
101 *
102 * @return void
103 */
104 public function append(string $metric) : void
105 {
106 if (!$this->canMetricBeAppended($metric)) {
107 return;
108 }
109 $this->metrics[$metric] = \true;
110 $this->metricsSize += \strlen($metric) + \strlen(self::$METRIC_SEPARATOR);
111 }
112 /**
113 * Receives a feature group and a value to identify which one is the metric.
114 * For example, a group could be `signature` and a value could be `v4a`,
115 * then the metric will be `SIGV4A_SIGNING`.
116 *
117 * @param string $featureGroup the feature group such as `signature`.
118 * @param mixed $value the value for identifying the metric.
119 *
120 * @return void
121 */
122 public function identifyMetricByValueAndAppend(string $featureGroup, mixed $value) : void
123 {
124 if (empty($value)) {
125 return;
126 }
127 static $appendMetricFns = ['signature' => 'appendSignatureMetric', 'request_compression' => 'appendRequestCompressionMetric', 'request_checksum' => 'appendRequestChecksumMetric', 'credentials' => 'appendCredentialsMetric', 'account_id_endpoint_mode' => 'appendAccountIdEndpointMode', 'account_id_endpoint' => 'appendAccountIdEndpoint', 'request_checksum_calculation' => 'appendRequestChecksumCalculationMetric', 'token' => 'appendTokenMetric'];
128 $fn = $appendMetricFns[$featureGroup];
129 $this->{$fn}($value);
130 }
131 /**
132 * Appends the signature metric based on the signature value.
133 *
134 * @param string $signature
135 *
136 * @return void
137 */
138 private function appendSignatureMetric(string $signature) : void
139 {
140 if ($signature === 'v4-s3express') {
141 $this->append(self::S3_EXPRESS_BUCKET);
142 } elseif ($signature === 'v4a') {
143 $this->append(self::SIGV4A_SIGNING);
144 }
145 }
146 /**
147 * Appends the request compression metric based on the format resolved.
148 *
149 * @param string $format
150 *
151 * @return void
152 */
153 private function appendRequestCompressionMetric(string $format) : void
154 {
155 if ($format === 'gzip') {
156 $this->append(self::GZIP_REQUEST_COMPRESSION);
157 }
158 }
159 /**
160 * Appends the request checksum metric based on the algorithm.
161 *
162 * @param string $algorithm
163 *
164 * @return void
165 */
166 private function appendRequestChecksumMetric(string $algorithm) : void
167 {
168 if ($algorithm === 'crc32') {
169 $this->append(self::FLEXIBLE_CHECKSUMS_REQ_CRC32);
170 } elseif ($algorithm === 'crc32c') {
171 $this->append(self::FLEXIBLE_CHECKSUMS_REQ_CRC32C);
172 } elseif ($algorithm === 'crc64') {
173 $this->append(self::FLEXIBLE_CHECKSUMS_REQ_CRC64);
174 } elseif ($algorithm === 'sha1') {
175 $this->append(self::FLEXIBLE_CHECKSUMS_REQ_SHA1);
176 } elseif ($algorithm === 'sha256') {
177 $this->append(self::FLEXIBLE_CHECKSUMS_REQ_SHA256);
178 }
179 }
180 /**
181 * Appends the credentials metric based on the type of credentials
182 * resolved.
183 *
184 * @param CredentialsInterface $credentials
185 *
186 * @return void
187 */
188 private function appendCredentialsMetric(CredentialsInterface $credentials) : void
189 {
190 $source = $credentials->toArray()['source'] ?? null;
191 if (empty($source)) {
192 return;
193 }
194 static $credentialsMetricMapping = [CredentialSources::STATIC => self::CREDENTIALS_CODE, CredentialSources::ENVIRONMENT => self::CREDENTIALS_ENV_VARS, CredentialSources::ENVIRONMENT_STS_WEB_ID_TOKEN => self::CREDENTIALS_ENV_VARS_STS_WEB_ID_TOKEN, CredentialSources::STS_ASSUME_ROLE => self::CREDENTIALS_STS_ASSUME_ROLE, CredentialSources::STS_WEB_ID_TOKEN => self::CREDENTIALS_STS_ASSUME_ROLE_WEB_ID, CredentialSources::PROFILE => self::CREDENTIALS_PROFILE, CredentialSources::IMDS => self::CREDENTIALS_IMDS, CredentialSources::ECS => self::CREDENTIALS_HTTP, CredentialSources::PROFILE_STS_WEB_ID_TOKEN => self::CREDENTIALS_PROFILE_STS_WEB_ID_TOKEN, CredentialSources::PROFILE_PROCESS => self::CREDENTIALS_PROFILE_PROCESS, CredentialSources::PROFILE_SSO => self::CREDENTIALS_PROFILE_SSO, CredentialSources::PROFILE_SSO_LEGACY => self::CREDENTIALS_PROFILE_SSO_LEGACY];
195 if (isset($credentialsMetricMapping[$source])) {
196 $this->append($credentialsMetricMapping[$source]);
197 }
198 }
199 private function appendTokenMetric(TokenInterface $token) : void
200 {
201 $source = $token->getSource();
202 if (empty($source)) {
203 return;
204 }
205 static $tokenMetricMapping = ['bearer_service_env_vars' => self::BEARER_SERVICE_ENV_VARS];
206 if (isset($tokenMetricMapping[$source])) {
207 $this->append($tokenMetricMapping[$source]);
208 }
209 }
210 private function appendRequestChecksumCalculationMetric(string $checkSumCalculation) : void
211 {
212 static $checksumCalculationMetricMapping = ['when_supported' => self::FLEXIBLE_CHECKSUMS_REQ_WHEN_SUPPORTED, 'when_required' => self::FLEXIBLE_CHECKSUMS_REQ_WHEN_REQUIRED];
213 if (isset($checksumCalculationMetricMapping[$checkSumCalculation])) {
214 $this->append($checksumCalculationMetricMapping[$checkSumCalculation]);
215 }
216 }
217 /**
218 * Appends the account_id_endpoint_mode metrics based on
219 * the value resolved.
220 *
221 * @param string $accountIdEndpointMode
222 *
223 * @return void
224 */
225 private function appendAccountIdEndpointMode(string $accountIdEndpointMode) : void
226 {
227 if (empty($accountIdEndpointMode)) {
228 return;
229 }
230 if ($accountIdEndpointMode === 'preferred') {
231 $this->append(self::ACCOUNT_ID_MODE_PREFERRED);
232 } elseif ($accountIdEndpointMode === 'disabled') {
233 $this->append(self::ACCOUNT_ID_MODE_DISABLED);
234 } elseif ($accountIdEndpointMode === 'required') {
235 $this->append(self::ACCOUNT_ID_MODE_REQUIRED);
236 }
237 }
238 /**
239 * Appends the account_id_endpoint metric whenever a resolved endpoint
240 * matches an account_id endpoint pattern which also defined here.
241 *
242 * @param string $endpoint
243 *
244 * @return void
245 */
246 private function appendAccountIdEndpoint(string $endpoint) : void
247 {
248 static $pattern = "/(https|http):\\/\\/\\d{12}\\.ddb/";
249 if (\preg_match($pattern, $endpoint)) {
250 $this->append(self::ACCOUNT_ID_ENDPOINT);
251 }
252 }
253 /**
254 * Resolves metrics from client arguments.
255 *
256 * @param array $args
257 *
258 * @return void
259 */
260 public function resolveAndAppendFromArgs(array $args = []) : void
261 {
262 static $metricsFnList = ['appendEndpointMetric', 'appendRetryConfigMetric', 'appendResponseChecksumValidationMetric'];
263 foreach ($metricsFnList as $metricFn) {
264 $this->{$metricFn}($args);
265 }
266 }
267 /**
268 * Appends the endpoint metric into the metrics builder,
269 * just if a custom endpoint was provided at client construction.
270 *
271 * @param array $args
272 *
273 * @return void
274 */
275 private function appendEndpointMetric(array $args) : void
276 {
277 if (!empty($args['endpoint_override'])) {
278 $this->append(MetricsBuilder::ENDPOINT_OVERRIDE);
279 }
280 }
281 /**
282 * Appends the retry mode metric into the metrics builder,
283 * based on the resolved retry config mode.
284 *
285 * @param array $args
286 *
287 * @return void
288 */
289 private function appendRetryConfigMetric(array $args) : void
290 {
291 $retries = $args['retries'] ?? null;
292 if ($retries === null) {
293 return;
294 }
295 $retryMode = '';
296 if ($retries instanceof \Dudlewebs\WPMCS\s3\Aws\Retry\Configuration) {
297 $retryMode = $retries->getMode();
298 } elseif (\is_array($retries) && isset($retries["mode"])) {
299 $retryMode = $retries["mode"];
300 }
301 if ($retryMode === 'legacy') {
302 $this->append(MetricsBuilder::RETRY_MODE_LEGACY);
303 } elseif ($retryMode === 'standard') {
304 $this->append(MetricsBuilder::RETRY_MODE_STANDARD);
305 } elseif ($retryMode === 'adaptive') {
306 $this->append(MetricsBuilder::RETRY_MODE_ADAPTIVE);
307 }
308 }
309 /**
310 * Appends the provided/resolved response checksum validation mode.
311 *
312 * @param array $args
313 *
314 * @return void
315 */
316 private function appendResponseChecksumValidationMetric(array $args) : void
317 {
318 if (empty($args['response_checksum_validation'])) {
319 return;
320 }
321 $checksumValidation = $args['response_checksum_validation'];
322 static $checksumValidationMetricMapping = ['when_supported' => MetricsBuilder::FLEXIBLE_CHECKSUMS_RES_WHEN_SUPPORTED, 'when_required' => MetricsBuilder::FLEXIBLE_CHECKSUMS_RES_WHEN_REQUIRED];
323 if (isset($checksumValidationMetricMapping[$checksumValidation])) {
324 $this->append($checksumValidationMetricMapping[$checksumValidation]);
325 }
326 }
327 /**
328 * Validates if a metric can be appended by ensuring the total size,
329 * including the new metric and separator, does not exceed the limit.
330 * Also checks that the metric does not already exist.
331 * Example: Appendable if:
332 * $currentSize + len($newMetric) + len($separator) <= MAX_SIZE
333 * and:
334 * $newMetric not in $existingMetrics
335 *
336 * @param string $newMetric The metric to validate.
337 *
338 * @return bool True if the metric can be appended, false otherwise.
339 */
340 private function canMetricBeAppended(string $newMetric) : bool
341 {
342 if ($newMetric === "") {
343 return \false;
344 }
345 if ($this->metricsSize + (\strlen($newMetric) + \strlen(self::$METRIC_SEPARATOR)) > self::$MAX_METRICS_SIZE) {
346 return \false;
347 }
348 if (isset($this->metrics[$newMetric])) {
349 return \false;
350 }
351 return \true;
352 }
353 /**
354 * Returns the metrics builder from the property @context of a command.
355 *
356 * @param Command $command
357 *
358 * @return MetricsBuilder
359 */
360 public static function fromCommand(CommandInterface $command) : MetricsBuilder
361 {
362 return $command->getMetricsBuilder();
363 }
364 /**
365 * Helper method for appending a metrics capture middleware into a
366 * handler stack given. The middleware appended here is on top of the
367 * build step.
368 *
369 * @param HandlerList $handlerList
370 * @param $metric
371 *
372 * @return void
373 */
374 public static function appendMetricsCaptureMiddleware(HandlerList $handlerList, $metric) : void
375 {
376 $middlewareName = 'metrics-capture-' . $metric;
377 if (!$handlerList->hasMiddleware($middlewareName)) {
378 $handlerList->appendBuild(Middleware::tap(function (CommandInterface $command) use($metric) {
379 self::fromCommand($command)->append($metric);
380 }), $middlewareName);
381 }
382 }
383 }
384