Passed
Branch master (3c33c7)
by Andrey
04:31
created

GuzzleHandler::__construct()   A

Complexity

Conditions 2
Paths 1

Size

Total Lines 3
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 2
eloc 1
c 0
b 0
f 0
nc 1
nop 1
dl 0
loc 3
rs 10
1
<?php
2
namespace Aws\Handler\GuzzleV5;
3
4
use Aws\Sdk;
5
use Exception;
6
use GuzzleHttp\Client;
7
use GuzzleHttp\ClientInterface;
8
use GuzzleHttp\Event\EndEvent;
0 ignored issues
show
Bug introduced by
The type GuzzleHttp\Event\EndEvent was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
9
use GuzzleHttp\Exception\ConnectException;
10
use GuzzleHttp\Exception\RequestException;
11
use GuzzleHttp\Message\ResponseInterface as GuzzleResponse;
0 ignored issues
show
Bug introduced by
The type GuzzleHttp\Message\ResponseInterface was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
12
use GuzzleHttp\Promise;
13
use GuzzleHttp\Psr7\Response as Psr7Response;
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
        'verify'          => true,
31
        'timeout'         => true,
32
        'debug'           => true,
33
        'connect_timeout' => true,
34
        'stream'          => true,
35
        'delay'           => true,
36
        'sink'            => true,
37
    ];
38
39
    /** @var ClientInterface */
40
    private $client;
41
42
    /**
43
     * @param ClientInterface $client
44
     */
45
    public function __construct(ClientInterface $client = null)
46
    {
47
        $this->client = $client ?: new Client();
48
    }
49
50
    /**
51
     * @param Psr7Request $request
52
     * @param array       $options
53
     *
54
     * @return Promise\Promise
55
     */
56
    public function __invoke(Psr7Request $request, array $options = [])
57
    {
58
        // Create and send a Guzzle 5 request
59
        $guzzlePromise = $this->client->send(
60
            $this->createGuzzleRequest($request, $options)
61
        );
62
63
        $promise = new Promise\Promise(
64
            function () use ($guzzlePromise) {
65
                try {
66
                    $guzzlePromise->wait();
0 ignored issues
show
Bug introduced by
The method wait() does not exist on Psr\Http\Message\ResponseInterface. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

66
                    $guzzlePromise->/** @scrutinizer ignore-call */ 
67
                                    wait();

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
67
                } catch (\Exception $e) {
68
                    // The promise is already delivered when the exception is
69
                    // thrown, so don't rethrow it.
70
                }
71
            },
72
            [$guzzlePromise, 'cancel']
73
        );
74
75
        $guzzlePromise->then([$promise, 'resolve'], [$promise, 'reject']);
0 ignored issues
show
Bug introduced by
The method then() does not exist on Psr\Http\Message\ResponseInterface. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

75
        $guzzlePromise->/** @scrutinizer ignore-call */ 
76
                        then([$promise, 'resolve'], [$promise, 'reject']);

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
76
77
        return $promise->then(
78
            function (GuzzleResponse $response) {
79
                // Adapt the Guzzle 5 Future to a Guzzle 6 ResponsePromise.
80
                return $this->createPsr7Response($response);
81
            },
82
            function (Exception $exception) {
83
                // Reject with information about the error.
84
                return new Promise\RejectedPromise($this->prepareErrorData($exception));
85
            }
86
        );
87
    }
88
89
    private function createGuzzleRequest(Psr7Request $psrRequest, array $options)
90
    {
91
        $ringConfig = [];
92
        $statsCallback = isset($options['http_stats_receiver'])
93
            ? $options['http_stats_receiver']
94
            : null;
95
        unset($options['http_stats_receiver']);
96
97
        // Remove unsupported options.
98
        foreach (array_keys($options) as $key) {
99
            if (!isset(self::$validOptions[$key])) {
100
                unset($options[$key]);
101
            }
102
        }
103
104
        // Handle delay option.
105
        if (isset($options['delay'])) {
106
            $ringConfig['delay'] = $options['delay'];
107
            unset($options['delay']);
108
        }
109
110
        // Prepare sink option.
111
        if (isset($options['sink'])) {
112
            $ringConfig['save_to'] = ($options['sink'] instanceof Psr7StreamInterface)
113
                ? new GuzzleStream($options['sink'])
114
                : $options['sink'];
115
            unset($options['sink']);
116
        }
117
118
        // Ensure that all requests are async and lazy like Guzzle 6.
119
        $options['future'] = 'lazy';
120
121
        // Create the Guzzle 5 request from the provided PSR7 request.
122
        $request = $this->client->createRequest(
123
            $psrRequest->getMethod(),
124
            $psrRequest->getUri(),
125
            $options
126
        );
127
128
        if (is_callable($statsCallback)) {
129
            $request->getEmitter()->on(
130
                'end',
131
                function (EndEvent $event) use ($statsCallback) {
132
                    $statsCallback($event->getTransferInfo());
133
                }
134
            );
135
        }
136
137
        // For the request body, adapt the PSR stream to a Guzzle stream.
138
        $body = $psrRequest->getBody();
139
        if ($body->getSize() === 0) {
140
            $request->setBody(null);
141
        } else {
142
            $request->setBody(new GuzzleStream($body));
143
        }
144
145
        $request->setHeaders($psrRequest->getHeaders());
146
147
        $request->setHeader(
148
            'User-Agent',
149
            $request->getHeader('User-Agent')
150
                . ' ' . Client::getDefaultUserAgent()
0 ignored issues
show
Bug introduced by
The method getDefaultUserAgent() does not exist on GuzzleHttp\Client. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

150
                . ' ' . Client::/** @scrutinizer ignore-call */ getDefaultUserAgent()

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
151
        );
152
153
        // Make sure the delay is configured, if provided.
154
        if ($ringConfig) {
155
            foreach ($ringConfig as $k => $v) {
156
                $request->getConfig()->set($k, $v);
157
            }
158
        }
159
160
        return $request;
161
    }
162
163
    private function createPsr7Response(GuzzleResponse $response)
164
    {
165
        if ($body = $response->getBody()) {
166
            $body = new PsrStream($body);
167
        }
168
169
        return new Psr7Response(
170
            $response->getStatusCode(),
171
            $response->getHeaders(),
172
            $body,
173
            $response->getReasonPhrase()
174
        );
175
    }
176
177
    private function prepareErrorData(Exception $e)
178
    {
179
        $error = [
180
            'exception'        => $e,
181
            'connection_error' => false,
182
            'response'         => null,
183
        ];
184
185
        if ($e instanceof ConnectException) {
186
            $error['connection_error'] = true;
187
        }
188
189
        if ($e instanceof RequestException && $e->getResponse()) {
190
            $error['response'] = $this->createPsr7Response($e->getResponse());
191
        }
192
193
        return $error;
194
    }
195
}
196