Proyectos de Subversion Moodle

Rev

| Ultima modificación | Ver Log |

Rev Autor Línea Nro. Línea
1 efrain 1
<?php
2
namespace Aws\Handler\GuzzleV5;
3
 
4
use Exception;
5
use GuzzleHttp\Client;
6
use GuzzleHttp\ClientInterface;
7
use GuzzleHttp\Event\EndEvent;
8
use GuzzleHttp\Exception\ConnectException;
9
use GuzzleHttp\Exception\RequestException;
10
use GuzzleHttp\Message\ResponseInterface as GuzzleResponse;
11
use GuzzleHttp\Promise;
12
use GuzzleHttp\Psr7\Response as Psr7Response;
13
use GuzzleHttp\Stream\Stream;
14
use Psr\Http\Message\RequestInterface as Psr7Request;
15
use Psr\Http\Message\StreamInterface as Psr7StreamInterface;
16
 
17
/**
18
 * A request handler that sends PSR-7-compatible requests with Guzzle 5.
19
 *
20
 * The handler accepts a PSR-7 Request object and an array of transfer options
21
 * and returns a Guzzle 6 Promise. The promise is either resolved with a
22
 * PSR-7 Response object or rejected with an array of error data.
23
 *
24
 * @codeCoverageIgnore
25
 */
26
class GuzzleHandler
27
{
28
    private static $validOptions = [
29
        'proxy'             => true,
30
        'expect'            => true,
31
        'cert'              => true,
32
        'verify'            => true,
33
        'timeout'           => true,
34
        'debug'             => true,
35
        'connect_timeout'   => true,
36
        'stream'            => true,
37
        'delay'             => true,
38
        'sink'              => true,
39
    ];
40
 
41
    /** @var ClientInterface */
42
    private $client;
43
 
44
    /**
45
     * @param ClientInterface $client
46
     */
47
    public function __construct(ClientInterface $client = null)
48
    {
49
        $this->client = $client ?: new Client();
50
    }
51
 
52
    /**
53
     * @param Psr7Request $request
54
     * @param array $options
55
     * @return Promise\Promise|Promise\PromiseInterface
56
     * @throws \GuzzleHttp\Exception\GuzzleException
57
     */
58
    public function __invoke(Psr7Request $request, array $options = [])
59
    {
60
        // Create and send a Guzzle 5 request
61
        $guzzlePromise = $this->client->send(
62
            $this->createGuzzleRequest($request, $options)
63
        );
64
 
65
        $promise = new Promise\Promise(
66
            function () use ($guzzlePromise) {
67
                try {
68
                    $guzzlePromise->wait();
69
                } catch (\Exception $e) {
70
                    // The promise is already delivered when the exception is
71
                    // thrown, so don't rethrow it.
72
                }
73
            },
74
            [$guzzlePromise, 'cancel']
75
        );
76
 
77
        $guzzlePromise->then([$promise, 'resolve'], [$promise, 'reject']);
78
 
79
        return $promise->then(
80
            function (GuzzleResponse $response) {
81
                // Adapt the Guzzle 5 Future to a Guzzle 6 ResponsePromise.
82
                return $this->createPsr7Response($response);
83
            },
84
            function (Exception $exception) use ($options) {
85
                // If we got a 'sink' that's a path, set the response body to
86
                // the contents of the file. This will build the resulting
87
                // exception with more information.
88
                if ($exception instanceof RequestException) {
89
                    if (isset($options['sink'])) {
90
                        if (!($options['sink'] instanceof Psr7StreamInterface)) {
91
                            $exception->getResponse()->setBody(
92
                                Stream::factory(
93
                                    file_get_contents($options['sink'])
94
                                )
95
                            );
96
                        }
97
                    }
98
                }
99
                // Reject with information about the error.
100
                return new Promise\RejectedPromise($this->prepareErrorData($exception));
101
            }
102
        );
103
    }
104
 
105
    private function createGuzzleRequest(Psr7Request $psrRequest, array $options)
106
    {
107
        $ringConfig = [];
108
        $statsCallback = isset($options['http_stats_receiver'])
109
            ? $options['http_stats_receiver']
110
            : null;
111
        unset($options['http_stats_receiver']);
112
 
113
        // Remove unsupported options.
114
        foreach (array_keys($options) as $key) {
115
            if (!isset(self::$validOptions[$key])) {
116
                unset($options[$key]);
117
            }
118
        }
119
 
120
        // Handle delay option.
121
        if (isset($options['delay'])) {
122
            $ringConfig['delay'] = $options['delay'];
123
            unset($options['delay']);
124
        }
125
 
126
        // Prepare sink option.
127
        if (isset($options['sink'])) {
128
            $ringConfig['save_to'] = ($options['sink'] instanceof Psr7StreamInterface)
129
                ? new GuzzleStream($options['sink'])
130
                : $options['sink'];
131
            unset($options['sink']);
132
        }
133
 
134
        // Ensure that all requests are async and lazy like Guzzle 6.
135
        $options['future'] = 'lazy';
136
 
137
        // Create the Guzzle 5 request from the provided PSR7 request.
138
        $request = $this->client->createRequest(
139
            $psrRequest->getMethod(),
140
            $psrRequest->getUri(),
141
            $options
142
        );
143
 
144
        if (is_callable($statsCallback)) {
145
            $request->getEmitter()->on(
146
                'end',
147
                function (EndEvent $event) use ($statsCallback) {
148
                    $statsCallback($event->getTransferInfo());
149
                }
150
            );
151
        }
152
 
153
        // For the request body, adapt the PSR stream to a Guzzle stream.
154
        $body = $psrRequest->getBody();
155
        if ($body->getSize() === 0) {
156
            $request->setBody(null);
157
        } else {
158
            $request->setBody(new GuzzleStream($body));
159
        }
160
 
161
        $request->setHeaders($psrRequest->getHeaders());
162
 
163
        $request->setHeader(
164
            'User-Agent',
165
            $request->getHeader('User-Agent')
166
                . ' ' . Client::getDefaultUserAgent()
167
        );
168
 
169
        // Make sure the delay is configured, if provided.
170
        if ($ringConfig) {
171
            foreach ($ringConfig as $k => $v) {
172
                $request->getConfig()->set($k, $v);
173
            }
174
        }
175
 
176
        return $request;
177
    }
178
 
179
    private function createPsr7Response(GuzzleResponse $response)
180
    {
181
        if ($body = $response->getBody()) {
182
            $body = new PsrStream($body);
183
        }
184
 
185
        return new Psr7Response(
186
            $response->getStatusCode(),
187
            $response->getHeaders(),
188
            $body,
189
            $response->getReasonPhrase()
190
        );
191
    }
192
 
193
    private function prepareErrorData(Exception $e)
194
    {
195
        $error = [
196
            'exception'        => $e,
197
            'connection_error' => false,
198
            'response'         => null,
199
        ];
200
 
201
        if ($e instanceof ConnectException) {
202
            $error['connection_error'] = true;
203
        }
204
 
205
        if ($e instanceof RequestException && $e->getResponse()) {
206
            $error['response'] = $this->createPsr7Response($e->getResponse());
207
        }
208
 
209
        return $error;
210
    }
211
}