GitHub Access Token became invalid

It seems like the GitHub access token used for retrieving details about this repository from GitHub became invalid. This might prevent certain types of inspections from being run (in particular, everything related to pull requests).
Please ask an admin of your repository to re-new the access token on this website.
Passed
Push — master ( 5cefd1...492078 )
by Anton
04:08
created

Transaction::onResponse()   B

Complexity

Conditions 8
Paths 3

Size

Total Lines 17
Code Lines 6

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 8
eloc 6
c 1
b 0
f 0
nc 3
nop 3
dl 0
loc 17
rs 8.4444
1
<?php
2
3
namespace React\Http\Io;
4
5
use Psr\Http\Message\RequestInterface;
6
use Psr\Http\Message\ResponseInterface;
7
use Psr\Http\Message\UriInterface;
8
use RingCentral\Psr7\Request;
9
use RingCentral\Psr7\Uri;
10
use React\EventLoop\LoopInterface;
11
use React\Http\Message\ResponseException;
12
use React\Promise\Deferred;
13
use React\Promise\PromiseInterface;
14
use React\Stream\ReadableStreamInterface;
15
16
/**
17
 * @internal
18
 */
19
class Transaction
20
{
21
    private $sender;
22
    private $loop;
23
24
    // context: http.timeout (ini_get('default_socket_timeout'): 60)
25
    private $timeout;
26
27
    // context: http.follow_location (true)
28
    private $followRedirects = true;
29
30
    // context: http.max_redirects (10)
31
    private $maxRedirects = 10;
32
33
    // context: http.ignore_errors (false)
34
    private $obeySuccessCode = true;
35
36
    private $streaming = false;
37
38
    private $maximumSize = 16777216; // 16 MiB = 2^24 bytes
39
40
    public function __construct(Sender $sender, LoopInterface $loop)
41
    {
42
        $this->sender = $sender;
43
        $this->loop = $loop;
44
    }
45
46
    /**
47
     * @param array $options
48
     * @return self returns new instance, without modifying existing instance
49
     */
50
    public function withOptions(array $options)
51
    {
52
        $transaction = clone $this;
53
        foreach ($options as $name => $value) {
54
            if (property_exists($transaction, $name)) {
55
                // restore default value if null is given
56
                if ($value === null) {
57
                    $default = new self($this->sender, $this->loop);
58
                    $value = $default->$name;
59
                }
60
61
                $transaction->$name = $value;
62
            }
63
        }
64
65
        return $transaction;
66
    }
67
68
    public function send(RequestInterface $request)
69
    {
70
        $deferred = new Deferred(function () use (&$deferred) {
71
            if (isset($deferred->pending)) {
72
                $deferred->pending->cancel();
73
                unset($deferred->pending);
74
            }
75
        });
76
77
        $deferred->numRequests = 0;
0 ignored issues
show
Bug introduced by
The property numRequests does not seem to exist on React\Promise\Deferred.
Loading history...
78
79
        // use timeout from options or default to PHP's default_socket_timeout (60)
80
        $timeout = (float)($this->timeout !== null ? $this->timeout : ini_get("default_socket_timeout"));
81
82
        $loop = $this->loop;
83
        $this->next($request, $deferred)->then(
84
            function (ResponseInterface $response) use ($deferred, $loop, &$timeout) {
85
                if (isset($deferred->timeout)) {
86
                    $loop->cancelTimer($deferred->timeout);
87
                    unset($deferred->timeout);
88
                }
89
                $timeout = -1;
90
                $deferred->resolve($response);
91
            },
92
            function ($e) use ($deferred, $loop, &$timeout) {
93
                if (isset($deferred->timeout)) {
94
                    $loop->cancelTimer($deferred->timeout);
95
                    unset($deferred->timeout);
96
                }
97
                $timeout = -1;
98
                $deferred->reject($e);
99
            }
100
        );
101
102
        if ($timeout < 0) {
103
            return $deferred->promise();
104
        }
105
106
        $body = $request->getBody();
107
        if ($body instanceof ReadableStreamInterface && $body->isReadable()) {
108
            $that = $this;
109
            $body->on('close', function () use ($that, $deferred, &$timeout) {
110
                if ($timeout >= 0) {
111
                    $that->applyTimeout($deferred, $timeout);
112
                }
113
            });
114
        } else {
115
            $this->applyTimeout($deferred, $timeout);
116
        }
117
118
        return $deferred->promise();
119
    }
120
121
    /**
122
     * @internal
123
     * @param Deferred $deferred
124
     * @param number  $timeout
125
     * @return void
126
     */
127
    public function applyTimeout(Deferred $deferred, $timeout)
128
    {
129
        $deferred->timeout = $this->loop->addTimer($timeout, function () use ($timeout, $deferred) {
0 ignored issues
show
Bug introduced by
The property timeout does not seem to exist on React\Promise\Deferred.
Loading history...
130
            $deferred->reject(new \RuntimeException(
131
                'Request timed out after ' . $timeout . ' seconds'
132
            ));
133
            if (isset($deferred->pending)) {
134
                $deferred->pending->cancel();
135
                unset($deferred->pending);
136
            }
137
        });
138
    }
139
140
    private function next(RequestInterface $request, Deferred $deferred)
141
    {
142
        $this->progress('request', array($request));
143
144
        $that = $this;
145
        ++$deferred->numRequests;
0 ignored issues
show
Bug introduced by
The property numRequests does not seem to exist on React\Promise\Deferred.
Loading history...
146
147
        $promise = $this->sender->send($request);
148
149
        if (!$this->streaming) {
150
            $promise = $promise->then(function ($response) use ($deferred, $that) {
151
                return $that->bufferResponse($response, $deferred);
152
            });
153
        }
154
155
        $deferred->pending = $promise;
0 ignored issues
show
Bug introduced by
The property pending does not seem to exist on React\Promise\Deferred.
Loading history...
156
157
        return $promise->then(
158
            function (ResponseInterface $response) use ($request, $that, $deferred) {
159
                return $that->onResponse($response, $request, $deferred);
160
            }
161
        );
162
    }
163
164
    /**
165
     * @internal
166
     * @param ResponseInterface $response
167
     * @return PromiseInterface Promise<ResponseInterface, Exception>
168
     */
169
    public function bufferResponse(ResponseInterface $response, $deferred)
170
    {
171
        $stream = $response->getBody();
172
173
        $size = $stream->getSize();
174
        if ($size !== null && $size > $this->maximumSize) {
175
            $stream->close();
176
            return \React\Promise\reject(new \OverflowException(
177
                'Response body size of ' . $size . ' bytes exceeds maximum of ' . $this->maximumSize . ' bytes',
178
                \defined('SOCKET_EMSGSIZE') ? \SOCKET_EMSGSIZE : 0
179
            ));
180
        }
181
182
        // body is not streaming => already buffered
183
        if (!$stream instanceof ReadableStreamInterface) {
184
            return \React\Promise\resolve($response);
0 ignored issues
show
Bug introduced by
$response of type Psr\Http\Message\ResponseInterface is incompatible with the type React\Promise\PromiseInterface expected by parameter $promiseOrValue of React\Promise\resolve(). ( Ignorable by Annotation )

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

184
            return \React\Promise\resolve(/** @scrutinizer ignore-type */ $response);
Loading history...
185
        }
186
187
        // buffer stream and resolve with buffered body
188
        $maximumSize = $this->maximumSize;
189
        $promise = \React\Promise\Stream\buffer($stream, $maximumSize)->then(
190
            function ($body) use ($response) {
191
                return $response->withBody(new BufferedBody($body));
192
            },
193
            function ($e) use ($stream, $maximumSize) {
194
                // try to close stream if buffering fails (or is cancelled)
195
                $stream->close();
196
197
                if ($e instanceof \OverflowException) {
198
                    $e = new \OverflowException(
199
                        'Response body size exceeds maximum of ' . $maximumSize . ' bytes',
200
                        \defined('SOCKET_EMSGSIZE') ? \SOCKET_EMSGSIZE : 0
201
                    );
202
                }
203
204
                throw $e;
205
            }
206
        );
207
208
        $deferred->pending = $promise;
209
210
        return $promise;
211
    }
212
213
    /**
214
     * @internal
215
     * @param ResponseInterface $response
216
     * @param RequestInterface $request
217
     * @throws ResponseException
218
     * @return ResponseInterface|PromiseInterface
219
     */
220
    public function onResponse(ResponseInterface $response, RequestInterface $request, $deferred)
221
    {
222
        $this->progress('response', array($response, $request));
223
224
        // follow 3xx (Redirection) response status codes if Location header is present and not explicitly disabled
225
        // @link https://tools.ietf.org/html/rfc7231#section-6.4
226
        if ($this->followRedirects && ($response->getStatusCode() >= 300 && $response->getStatusCode() < 400) && $response->hasHeader('Location')) {
227
            return $this->onResponseRedirect($response, $request, $deferred);
228
        }
229
230
        // only status codes 200-399 are considered to be valid, reject otherwise
231
        if ($this->obeySuccessCode && ($response->getStatusCode() < 200 || $response->getStatusCode() >= 400)) {
232
            throw new ResponseException($response);
233
        }
234
235
        // resolve our initial promise
236
        return $response;
237
    }
238
239
    /**
240
     * @param ResponseInterface $response
241
     * @param RequestInterface $request
242
     * @return PromiseInterface
243
     * @throws \RuntimeException
244
     */
245
    private function onResponseRedirect(ResponseInterface $response, RequestInterface $request, Deferred $deferred)
246
    {
247
        // resolve location relative to last request URI
248
        $location = Uri::resolve($request->getUri(), $response->getHeaderLine('Location'));
249
250
        $request = $this->makeRedirectRequest($request, $location);
251
        $this->progress('redirect', array($request));
252
253
        if ($deferred->numRequests >= $this->maxRedirects) {
0 ignored issues
show
Bug introduced by
The property numRequests does not seem to exist on React\Promise\Deferred.
Loading history...
254
            throw new \RuntimeException('Maximum number of redirects (' . $this->maxRedirects . ') exceeded');
255
        }
256
257
        return $this->next($request, $deferred);
258
    }
259
260
    /**
261
     * @param RequestInterface $request
262
     * @param UriInterface $location
263
     * @return RequestInterface
264
     */
265
    private function makeRedirectRequest(RequestInterface $request, UriInterface $location)
266
    {
267
        $originalHost = $request->getUri()->getHost();
268
        $request = $request
269
            ->withoutHeader('Host')
270
            ->withoutHeader('Content-Type')
271
            ->withoutHeader('Content-Length');
272
273
        // Remove authorization if changing hostnames (but not if just changing ports or protocols).
274
        if ($location->getHost() !== $originalHost) {
275
            $request = $request->withoutHeader('Authorization');
276
        }
277
278
        // naïve approach..
279
        $method = ($request->getMethod() === 'HEAD') ? 'HEAD' : 'GET';
280
281
        return new Request($method, $location, $request->getHeaders());
282
    }
283
284
    private function progress($name, array $args = array())
0 ignored issues
show
Unused Code introduced by
The parameter $args is not used and could be removed. ( Ignorable by Annotation )

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

284
    private function progress($name, /** @scrutinizer ignore-unused */ array $args = array())

This check looks for parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
Unused Code introduced by
The parameter $name is not used and could be removed. ( Ignorable by Annotation )

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

284
    private function progress(/** @scrutinizer ignore-unused */ $name, array $args = array())

This check looks for parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
285
    {
286
        return;
287
288
        echo $name;
0 ignored issues
show
Unused Code introduced by
echo $name is not reachable.

This check looks for unreachable code. It uses sophisticated control flow analysis techniques to find statements which will never be executed.

Unreachable code is most often the result of return, die or exit statements that have been added for debug purposes.

function fx() {
    try {
        doSomething();
        return true;
    }
    catch (\Exception $e) {
        return false;
    }

    return false;
}

In the above example, the last return false will never be executed, because a return statement has already been met in every possible execution path.

Loading history...
289
290
        foreach ($args as $arg) {
291
            echo ' ';
292
            if ($arg instanceof ResponseInterface) {
293
                echo 'HTTP/' . $arg->getProtocolVersion() . ' ' . $arg->getStatusCode() . ' ' . $arg->getReasonPhrase();
294
            } elseif ($arg instanceof RequestInterface) {
295
                echo $arg->getMethod() . ' ' . $arg->getRequestTarget() . ' HTTP/' . $arg->getProtocolVersion();
296
            } else {
297
                echo $arg;
298
            }
299
        }
300
301
        echo PHP_EOL;
302
    }
303
}
304