Proyectos de Subversion Moodle

Rev

Rev 1 | | Comparar con el anterior | Ultima modificación | Ver Log |

Rev Autor Línea Nro. Línea
1 efrain 1
<?php
2
namespace Aws;
3
 
4
use Aws\Api\Service;
5
use Aws\Api\Validator;
6
use Aws\Credentials\CredentialsInterface;
7
use Aws\EndpointV2\EndpointProviderV2;
8
use Aws\Exception\AwsException;
1441 ariadna 9
use Aws\Signature\S3ExpressSignature;
1 efrain 10
use Aws\Token\TokenAuthorization;
11
use Aws\Token\TokenInterface;
12
use GuzzleHttp\Promise;
13
use GuzzleHttp\Psr7;
14
use GuzzleHttp\Psr7\LazyOpenStream;
15
use Psr\Http\Message\RequestInterface;
16
 
17
final class Middleware
18
{
19
    /**
20
     * Middleware used to allow a command parameter (e.g., "SourceFile") to
21
     * be used to specify the source of data for an upload operation.
22
     *
23
     * @param Service $api
24
     * @param string  $bodyParameter
25
     * @param string  $sourceParameter
26
     *
27
     * @return callable
28
     */
29
    public static function sourceFile(
30
        Service $api,
31
        $bodyParameter = 'Body',
32
        $sourceParameter = 'SourceFile'
33
    ) {
34
        return function (callable $handler) use (
35
            $api,
36
            $bodyParameter,
37
            $sourceParameter
38
        ) {
39
            return function (
40
                CommandInterface $command,
1441 ariadna 41
                ?RequestInterface $request = null)
1 efrain 42
            use (
43
                $handler,
44
                $api,
45
                $bodyParameter,
46
                $sourceParameter
47
            ) {
48
                $operation = $api->getOperation($command->getName());
49
                $source = $command[$sourceParameter];
50
 
51
                if ($source !== null
52
                    && $operation->getInput()->hasMember($bodyParameter)
53
                ) {
1441 ariadna 54
                    $lazyOpenStream = new LazyOpenStream($source, 'r');
55
                    $command[$bodyParameter] = $lazyOpenStream;
1 efrain 56
                    unset($command[$sourceParameter]);
1441 ariadna 57
 
58
                    $next = $handler($command, $request);
59
                    // To avoid failures in some tests cases
60
                    if ($next !== null && method_exists($next, 'then')) {
61
                        return $next->then(
62
                            function ($result) use ($lazyOpenStream) {
63
                                // To make sure the resource is closed.
64
                                $lazyOpenStream->close();
65
 
66
                                return $result;
67
                            }
68
                        )->otherwise(function (\Throwable $e) use ($lazyOpenStream) {
69
                            $lazyOpenStream->close();
70
 
71
                            throw $e;
72
                        });
73
                    }
74
 
75
                    return $next;
1 efrain 76
                }
77
 
78
                return $handler($command, $request);
79
            };
80
        };
81
    }
82
 
83
    /**
84
     * Adds a middleware that uses client-side validation.
85
     *
86
     * @param Service $api API being accessed.
87
     *
88
     * @return callable
89
     */
1441 ariadna 90
    public static function validation(Service $api, ?Validator $validator = null)
1 efrain 91
    {
92
        $validator = $validator ?: new Validator();
93
        return function (callable $handler) use ($api, $validator) {
94
            return function (
95
                CommandInterface $command,
1441 ariadna 96
                ?RequestInterface $request = null
1 efrain 97
            ) use ($api, $validator, $handler) {
98
                if ($api->isModifiedModel()) {
99
                    $api = new Service(
100
                        $api->getDefinition(),
101
                        $api->getProvider()
102
                    );
103
                }
104
                $operation = $api->getOperation($command->getName());
105
                $validator->validate(
106
                    $command->getName(),
107
                    $operation->getInput(),
108
                    $command->toArray()
109
                );
110
                return $handler($command, $request);
111
            };
112
        };
113
    }
114
 
115
    /**
116
     * Builds an HTTP request for a command.
117
     *
118
     * @param callable $serializer Function used to serialize a request for a
119
     *                             command.
120
     * @param EndpointProviderV2 | null $endpointProvider
121
     * @param array $providerArgs
122
     * @return callable
123
     */
1441 ariadna 124
    public static function requestBuilder($serializer)
1 efrain 125
    {
1441 ariadna 126
        return function (callable $handler) use ($serializer) {
127
            return function (CommandInterface $command, $endpoint = null) use ($serializer, $handler) {
128
                return $handler($command, $serializer($command, $endpoint));
1 efrain 129
            };
130
        };
131
    }
132
 
133
    /**
134
     * Creates a middleware that signs requests for a command.
135
     *
136
     * @param callable $credProvider      Credentials provider function that
137
     *                                    returns a promise that is resolved
138
     *                                    with a CredentialsInterface object.
139
     * @param callable $signatureFunction Function that accepts a Command
140
     *                                    object and returns a
141
     *                                    SignatureInterface.
142
     *
143
     * @return callable
144
     */
1441 ariadna 145
    public static function signer(callable $credProvider, callable $signatureFunction, $tokenProvider = null, $config = [])
1 efrain 146
    {
1441 ariadna 147
        return function (callable $handler) use ($signatureFunction, $credProvider, $tokenProvider, $config) {
1 efrain 148
            return function (
149
                CommandInterface $command,
150
                RequestInterface $request
1441 ariadna 151
            ) use ($handler, $signatureFunction, $credProvider, $tokenProvider, $config) {
1 efrain 152
                $signer = $signatureFunction($command);
153
                if ($signer instanceof TokenAuthorization) {
154
                    return $tokenProvider()->then(
155
                        function (TokenInterface $token)
156
                        use ($handler, $command, $signer, $request) {
157
                            return $handler(
158
                                $command,
159
                                $signer->authorizeRequest($request, $token)
160
                            );
161
                        }
162
                    );
1441 ariadna 163
                }
164
 
165
                if ($signer instanceof S3ExpressSignature) {
166
                    $credentialPromise = $config['s3_express_identity_provider']($command);
1 efrain 167
                } else {
1441 ariadna 168
                    $credentialPromise = $credProvider();
1 efrain 169
                }
1441 ariadna 170
 
171
                return $credentialPromise->then(
172
                    function (CredentialsInterface $creds)
173
                    use ($handler, $command, $signer, $request) {
174
                        // Capture credentials metric
175
                        $command->getMetricsBuilder()->identifyMetricByValueAndAppend(
176
                            'credentials',
177
                            $creds
178
                        );
179
 
180
                        return $handler(
181
                            $command,
182
                            $signer->signRequest($request, $creds)
183
                        );
184
                    }
185
                );
1 efrain 186
            };
187
        };
188
    }
189
 
190
    /**
191
     * Creates a middleware that invokes a callback at a given step.
192
     *
193
     * The tap callback accepts a CommandInterface and RequestInterface as
194
     * arguments but is not expected to return a new value or proxy to
195
     * downstream middleware. It's simply a way to "tap" into the handler chain
196
     * to debug or get an intermediate value.
197
     *
198
     * @param callable $fn Tap function
199
     *
200
     * @return callable
201
     */
202
    public static function tap(callable $fn)
203
    {
204
        return function (callable $handler) use ($fn) {
205
            return function (
206
                CommandInterface $command,
1441 ariadna 207
                ?RequestInterface $request = null
1 efrain 208
            ) use ($handler, $fn) {
209
                $fn($command, $request);
210
                return $handler($command, $request);
211
            };
212
        };
213
    }
214
 
215
    /**
216
     * Middleware wrapper function that retries requests based on the boolean
217
     * result of invoking the provided "decider" function.
218
     *
219
     * If no delay function is provided, a simple implementation of exponential
220
     * backoff will be utilized.
221
     *
222
     * @param callable $decider Function that accepts the number of retries,
223
     *                          a request, [result], and [exception] and
224
     *                          returns true if the command is to be retried.
225
     * @param callable $delay   Function that accepts the number of retries and
226
     *                          returns the number of milliseconds to delay.
227
     * @param bool $stats       Whether to collect statistics on retries and the
228
     *                          associated delay.
229
     *
230
     * @return callable
231
     */
232
    public static function retry(
1441 ariadna 233
        ?callable $decider = null,
234
        ?callable $delay = null,
1 efrain 235
        $stats = false
236
    ) {
237
        $decider = $decider ?: RetryMiddleware::createDefaultDecider();
238
        $delay = $delay ?: [RetryMiddleware::class, 'exponentialDelay'];
239
 
240
        return function (callable $handler) use ($decider, $delay, $stats) {
241
            return new RetryMiddleware($decider, $delay, $handler, $stats);
242
        };
243
    }
244
    /**
245
     * Middleware wrapper function that adds an invocation id header to
246
     * requests, which is only applied after the build step.
247
     *
248
     * This is a uniquely generated UUID to identify initial and subsequent
249
     * retries as part of a complete request lifecycle.
250
     *
251
     * @return callable
252
     */
253
    public static function invocationId()
254
    {
255
        return function (callable $handler) {
256
            return function (
257
                CommandInterface $command,
258
                RequestInterface $request
259
            ) use ($handler){
260
                return $handler($command, $request->withHeader(
261
                    'aws-sdk-invocation-id',
262
                    md5(uniqid(gethostname(), true))
263
                ));
264
            };
265
        };
266
    }
267
    /**
268
     * Middleware wrapper function that adds a Content-Type header to requests.
269
     * This is only done when the Content-Type has not already been set, and the
270
     * request body's URI is available. It then checks the file extension of the
271
     * URI to determine the mime-type.
272
     *
273
     * @param array $operations Operations that Content-Type should be added to.
274
     *
275
     * @return callable
276
     */
277
    public static function contentType(array $operations)
278
    {
279
        return function (callable $handler) use ($operations) {
280
            return function (
281
                CommandInterface $command,
1441 ariadna 282
                ?RequestInterface $request = null
1 efrain 283
            ) use ($handler, $operations) {
284
                if (!$request->hasHeader('Content-Type')
285
                    && in_array($command->getName(), $operations, true)
286
                    && ($uri = $request->getBody()->getMetadata('uri'))
287
                ) {
288
                    $request = $request->withHeader(
289
                        'Content-Type',
290
                        Psr7\MimeType::fromFilename($uri) ?: 'application/octet-stream'
291
                    );
292
                }
293
 
294
                return $handler($command, $request);
295
            };
296
        };
297
    }
298
    /**
299
     * Middleware wrapper function that adds a trace id header to requests
300
     * from clients instantiated in supported Lambda runtime environments.
301
     *
302
     * The purpose for this header is to track and stop Lambda functions
303
     * from being recursively invoked due to misconfigured resources.
304
     *
305
     * @return callable
306
     */
307
    public static function recursionDetection()
308
    {
309
        return function (callable $handler) {
310
            return function (
311
                CommandInterface $command,
312
                RequestInterface $request
313
            ) use ($handler){
314
                $isLambda = getenv('AWS_LAMBDA_FUNCTION_NAME');
315
                $traceId = str_replace('\e', '\x1b', getenv('_X_AMZN_TRACE_ID'));
316
 
317
                if ($isLambda && $traceId) {
318
                    if (!$request->hasHeader('X-Amzn-Trace-Id')) {
319
                        $ignoreChars = ['=', ';', ':', '+', '&', '[', ']', '{', '}', '"', '\'', ','];
320
                        $traceIdEncoded = rawurlencode(stripcslashes($traceId));
321
 
322
                        foreach($ignoreChars as $char) {
323
                            $encodedChar = rawurlencode($char);
324
                            $traceIdEncoded = str_replace($encodedChar, $char,  $traceIdEncoded);
325
                        }
326
 
327
                        return $handler($command, $request->withHeader(
328
                            'X-Amzn-Trace-Id',
329
                            $traceIdEncoded
330
                        ));
331
                    }
332
                }
333
                return $handler($command, $request);
334
            };
335
        };
336
    }
337
    /**
338
     * Tracks command and request history using a history container.
339
     *
340
     * This is useful for testing.
341
     *
342
     * @param History $history History container to store entries.
343
     *
344
     * @return callable
345
     */
346
    public static function history(History $history)
347
    {
348
        return function (callable $handler) use ($history) {
349
            return function (
350
                CommandInterface $command,
1441 ariadna 351
                ?RequestInterface $request = null
1 efrain 352
            ) use ($handler, $history) {
353
                $ticket = $history->start($command, $request);
354
                return $handler($command, $request)
355
                    ->then(
356
                        function ($result) use ($history, $ticket) {
357
                            $history->finish($ticket, $result);
358
                            return $result;
359
                        },
360
                        function ($reason) use ($history, $ticket) {
361
                            $history->finish($ticket, $reason);
362
                            return Promise\Create::rejectionFor($reason);
363
                        }
364
                    );
365
            };
366
        };
367
    }
368
 
369
    /**
370
     * Creates a middleware that applies a map function to requests as they
371
     * pass through the middleware.
372
     *
373
     * @param callable $f Map function that accepts a RequestInterface and
374
     *                    returns a RequestInterface.
375
     *
376
     * @return callable
377
     */
378
    public static function mapRequest(callable $f)
379
    {
380
        return function (callable $handler) use ($f) {
381
            return function (
382
                CommandInterface $command,
1441 ariadna 383
                ?RequestInterface $request = null
1 efrain 384
            ) use ($handler, $f) {
385
                return $handler($command, $f($request));
386
            };
387
        };
388
    }
389
 
390
    /**
391
     * Creates a middleware that applies a map function to commands as they
392
     * pass through the middleware.
393
     *
394
     * @param callable $f Map function that accepts a command and returns a
395
     *                    command.
396
     *
397
     * @return callable
398
     */
399
    public static function mapCommand(callable $f)
400
    {
401
        return function (callable $handler) use ($f) {
402
            return function (
403
                CommandInterface $command,
1441 ariadna 404
                ?RequestInterface $request = null
1 efrain 405
            ) use ($handler, $f) {
406
                return $handler($f($command), $request);
407
            };
408
        };
409
    }
410
 
411
    /**
412
     * Creates a middleware that applies a map function to results.
413
     *
414
     * @param callable $f Map function that accepts an Aws\ResultInterface and
415
     *                    returns an Aws\ResultInterface.
416
     *
417
     * @return callable
418
     */
419
    public static function mapResult(callable $f)
420
    {
421
        return function (callable $handler) use ($f) {
422
            return function (
423
                CommandInterface $command,
1441 ariadna 424
                ?RequestInterface $request = null
1 efrain 425
            ) use ($handler, $f) {
426
                return $handler($command, $request)->then($f);
427
            };
428
        };
429
    }
430
 
431
    public static function timer()
432
    {
433
        return function (callable $handler) {
434
            return function (
435
                CommandInterface $command,
1441 ariadna 436
                ?RequestInterface $request = null
1 efrain 437
            ) use ($handler) {
438
                $start = microtime(true);
439
                return $handler($command, $request)
440
                    ->then(
441
                        function (ResultInterface $res) use ($start) {
442
                            if (!isset($res['@metadata'])) {
443
                                $res['@metadata'] = [];
444
                            }
445
                            if (!isset($res['@metadata']['transferStats'])) {
446
                                $res['@metadata']['transferStats'] = [];
447
                            }
448
 
449
                            $res['@metadata']['transferStats']['total_time']
450
                                = microtime(true) - $start;
451
 
452
                            return $res;
453
                        },
454
                        function ($err) use ($start) {
455
                            if ($err instanceof AwsException) {
456
                                $err->setTransferInfo([
457
                                    'total_time' => microtime(true) - $start,
458
                                ] + $err->getTransferInfo());
459
                            }
460
                            return Promise\Create::rejectionFor($err);
461
                        }
462
                    );
463
            };
464
        };
465
    }
466
}