Proyectos de Subversion Moodle

Rev

| Ultima modificación | Ver Log |

Rev Autor Línea Nro. Línea
1 efrain 1
<?php
2
namespace Aws;
3
 
4
use Aws\Api\Parser\Exception\ParserException;
5
use Aws\Exception\AwsException;
6
use GuzzleHttp\Promise;
7
use Psr\Http\Message\RequestInterface;
8
use Psr\Http\Message\ResponseInterface;
9
 
10
/**
11
 * Converts an HTTP handler into a Command HTTP handler.
12
 *
13
 * HTTP handlers have the following signature:
14
 *     function(RequestInterface $request, array $options) : PromiseInterface
15
 *
16
 * The promise returned form an HTTP handler must resolve to a PSR-7 response
17
 * object when fulfilled or an error array when rejected. The error array
18
 * can contain the following data:
19
 *
20
 * - exception: (required, Exception) Exception that was encountered.
21
 * - response: (ResponseInterface) PSR-7 response that was received (if a
22
 *   response) was received.
23
 * - connection_error: (bool) True if the error is the result of failing to
24
 *   connect.
25
 */
26
class WrappedHttpHandler
27
{
28
    private $httpHandler;
29
    private $parser;
30
    private $errorParser;
31
    private $exceptionClass;
32
    private $collectStats;
33
 
34
    /**
35
     * @param callable $httpHandler    Function that accepts a request and array
36
     *                                 of request options and returns a promise
37
     *                                 that fulfills with a response or rejects
38
     *                                 with an error array.
39
     * @param callable $parser         Function that accepts a response object
40
     *                                 and returns an AWS result object.
41
     * @param callable $errorParser    Function that parses a response object
42
     *                                 into AWS error data.
43
     * @param string   $exceptionClass Exception class to throw.
44
     * @param bool     $collectStats   Whether to collect HTTP transfer
45
     *                                 information.
46
     */
47
    public function __construct(
48
        callable $httpHandler,
49
        callable $parser,
50
        callable $errorParser,
51
        $exceptionClass = AwsException::class,
52
        $collectStats = false
53
    ) {
54
        $this->httpHandler = $httpHandler;
55
        $this->parser = $parser;
56
        $this->errorParser = $errorParser;
57
        $this->exceptionClass = $exceptionClass;
58
        $this->collectStats = $collectStats;
59
    }
60
 
61
    /**
62
     * Calls the simpler HTTP specific handler and wraps the returned promise
63
     * with AWS specific values (e.g., a result object or AWS exception).
64
     *
65
     * @param CommandInterface $command Command being executed.
66
     * @param RequestInterface $request Request to send.
67
     *
68
     * @return Promise\PromiseInterface
69
     */
70
    public function __invoke(
71
        CommandInterface $command,
72
        RequestInterface $request
73
    ) {
74
        $fn = $this->httpHandler;
75
        $options = $command['@http'] ?: [];
76
        $stats = [];
77
        if ($this->collectStats || !empty($options['collect_stats'])) {
78
            $options['http_stats_receiver'] = static function (
79
                array $transferStats
80
            ) use (&$stats) {
81
                $stats = $transferStats;
82
            };
83
        } elseif (isset($options['http_stats_receiver'])) {
84
            throw new \InvalidArgumentException('Providing a custom HTTP stats'
85
                . ' receiver to Aws\WrappedHttpHandler is not supported.');
86
        }
87
 
88
        return Promise\Create::promiseFor($fn($request, $options))
89
            ->then(
90
                function (
91
                    ResponseInterface $res
92
                ) use ($command, $request, &$stats) {
93
                    return $this->parseResponse($command, $request, $res, $stats);
94
                },
95
                function ($err) use ($request, $command, &$stats) {
96
                    if (is_array($err)) {
97
                        $err = $this->parseError(
98
                            $err,
99
                            $request,
100
                            $command,
101
                            $stats
102
                        );
103
                    }
104
                    return new Promise\RejectedPromise($err);
105
                }
106
            );
107
    }
108
 
109
    /**
110
     * @param CommandInterface  $command
111
     * @param RequestInterface  $request
112
     * @param ResponseInterface $response
113
     * @param array             $stats
114
     *
115
     * @return ResultInterface
116
     */
117
    private function parseResponse(
118
        CommandInterface $command,
119
        RequestInterface $request,
120
        ResponseInterface $response,
121
        array $stats
122
    ) {
123
        $parser = $this->parser;
124
        $status = $response->getStatusCode();
125
        $result = $status < 300
126
            ? $parser($command, $response)
127
            : new Result();
128
 
129
        $metadata = [
130
            'statusCode'    => $status,
131
            'effectiveUri'  => (string) $request->getUri(),
132
            'headers'       => [],
133
            'transferStats' => [],
134
        ];
135
        if (!empty($stats)) {
136
            $metadata['transferStats']['http'] = [$stats];
137
        }
138
 
139
        // Bring headers into the metadata array.
140
        foreach ($response->getHeaders() as $name => $values) {
141
            $metadata['headers'][strtolower($name)] = $values[0];
142
        }
143
 
144
        $result['@metadata'] = $metadata;
145
 
146
        return $result;
147
    }
148
 
149
    /**
150
     * Parses a rejection into an AWS error.
151
     *
152
     * @param array            $err     Rejection error array.
153
     * @param RequestInterface $request Request that was sent.
154
     * @param CommandInterface $command Command being sent.
155
     * @param array            $stats   Transfer statistics
156
     *
157
     * @return \Exception
158
     */
159
    private function parseError(
160
        array $err,
161
        RequestInterface $request,
162
        CommandInterface $command,
163
        array $stats
164
    ) {
165
        if (!isset($err['exception'])) {
166
            throw new \RuntimeException('The HTTP handler was rejected without an "exception" key value pair.');
167
        }
168
 
169
        $serviceError = "AWS HTTP error: " . $err['exception']->getMessage();
170
 
171
        if (!isset($err['response'])) {
172
            $parts = ['response' => null];
173
        } else {
174
            try {
175
                $parts = call_user_func(
176
                    $this->errorParser,
177
                    $err['response'],
178
                    $command
179
                );
180
                $serviceError .= " {$parts['code']} ({$parts['type']}): "
181
                    . "{$parts['message']} - " . $err['response']->getBody();
182
            } catch (ParserException $e) {
183
                $parts = [];
184
                $serviceError .= ' Unable to parse error information from '
185
                    . "response - {$e->getMessage()}";
186
            }
187
 
188
            $parts['response'] = $err['response'];
189
        }
190
 
191
        $parts['exception'] = $err['exception'];
192
        $parts['request'] = $request;
193
        $parts['connection_error'] = !empty($err['connection_error']);
194
        $parts['transfer_stats'] = $stats;
195
 
196
        return new $this->exceptionClass(
197
            sprintf(
198
                'Error executing "%s" on "%s"; %s',
199
                $command->getName(),
200
                $request->getUri(),
201
                $serviceError
202
            ),
203
            $command,
204
            $parts,
205
            $err['exception']
206
        );
207
    }
208
}