Completed
Push — master ( 12db63...b50b89 )
by adam
02:23
created

MiddlewareFactory   A

Complexity

Total Complexity 15

Size/Duplication

Total Lines 99
Duplicated Lines 0 %

Coupling/Cohesion

Components 0
Dependencies 5

Test Coverage

Coverage 92.45%

Importance

Changes 2
Bugs 0 Features 0
Metric Value
wmc 15
c 2
b 0
f 0
lcom 0
cbo 5
dl 0
loc 99
ccs 49
cts 53
cp 0.9245
rs 10

4 Methods

Rating   Name   Duplication   Size   Complexity  
A __construct() 0 3 1
A setLogger() 0 3 1
A retry() 0 3 1
C newRetryDecider() 0 72 12
1
<?php
2
3
namespace Mediawiki\Api\Guzzle;
4
5
use GuzzleHttp\Exception\ConnectException;
6
use GuzzleHttp\Exception\RequestException;
7
use GuzzleHttp\Middleware;
8
use GuzzleHttp\Psr7\Request;
9
use GuzzleHttp\Psr7\Response;
10
use Psr\Log\LoggerAwareInterface;
11
use Psr\Log\LoggerInterface;
12
use Psr\Log\NullLogger;
13
14
/**
15
 * @access private
16
 *
17
 * @author Addshore
18
 */
19
class MiddlewareFactory implements LoggerAwareInterface {
20
21
	/**
22
	 * @var LoggerInterface
23
	 */
24
	private $logger;
25
26 5
	public function __construct() {
27 5
		$this->logger = new NullLogger();
28 5
	}
29
30
	public function setLogger( LoggerInterface $logger ) {
31
		$this->logger = $logger;
32
	}
33
34
	/**
35
	 * @return callable
36
	 */
37 5
	public function retry() {
38 5
		return Middleware::retry( $this->newRetryDecider() );
39
	}
40
41
	/**
42
	 * @return callable
43
	 */
44
	private function newRetryDecider() {
45 5
		return function (
46
			$retries,
47
			Request $request,
48
			Response $response = null,
49
			RequestException $exception = null
50
		) {
51
			// Don't retry if we have run out of retries
52 5
			if ( $retries >= 5 ) {
53 1
				return false;
54
			}
55
56 5
			$shouldRetry = false;
57
58
			// Retry connection exceptions
59 5
			if( $exception instanceof ConnectException ) {
60 2
				$shouldRetry = true;
61 2
			}
62
63 5
			if( $response ) {
64 4
				$headers = $response->getHeaders();
65 4
				$data = json_decode( $response->getBody(), true );
66
67
				// Retry on server errors
68 4
				if( $response->getStatusCode() >= 500 ) {
69 1
					$shouldRetry = true;
70 1
				}
71
72 4
				if ( array_key_exists( 'mediawiki-api-error', $headers ) ) {
73 2
					foreach( $headers['mediawiki-api-error'] as $mediawikiApiErrorHeader ) {
74
						if (
75
							// Retry if we have a response with an API error worth retrying
76 2
							in_array(
77 2
								$mediawikiApiErrorHeader,
78
								array(
79 2
									'ratelimited',
80 3
									'readonly',
81 2
									'internal_api_error_DBQueryError',
82
								)
83 2
							)
84
							||
85
							// Or if we have been stopped from saving as an 'anti-abuse measure'
86
							// Note: this tries to match "actionthrottledtext" i18n messagae for mediawiki
87
							(
88 2
								$mediawikiApiErrorHeader == 'failed-save' &&
89 1
								strstr( $data['error']['info'], 'anti-abuse measure' )
90 1
							)
91 2
						) {
92 2
							$shouldRetry = true;
93 2
						}
94
95 2
					}
96 2
				}
97 4
			}
98
99
			// Log if we are retrying
100 5
			if( $shouldRetry ) {
101 5
				$this->logger->warning(
102 5
					sprintf(
103 5
						'Retrying %s %s %s/5, %s',
104 5
						$request->getMethod(),
105 5
						$request->getUri(),
106 5
						$retries + 1,
107 5
						$response ? 'status code: ' . $response->getStatusCode() :
108 2
							$exception->getMessage()
0 ignored issues
show
Bug introduced by
It seems like $exception is not always an object, but can also be of type null. Maybe add an additional type check?

If a variable is not always an object, we recommend to add an additional type check to ensure your method call is safe:

function someFunction(A $objectMaybe = null)
{
    if ($objectMaybe instanceof A) {
        $objectMaybe->doSomething();
    }
}
Loading history...
109 5
					)
110 5
				);
111 5
			}
112
113 5
			return $shouldRetry;
114 5
		};
115
	}
116
117
}
118