1
|
|
|
<?php |
2
|
|
|
|
3
|
|
|
declare(strict_types=1); |
4
|
|
|
|
5
|
|
|
namespace Http\Client\Common\HttpClientPool; |
6
|
|
|
|
7
|
|
|
use Http\Client\Common\FlexibleHttpClient; |
8
|
|
|
use Http\Client\Exception; |
9
|
|
|
use Http\Client\HttpAsyncClient; |
10
|
|
|
use Http\Client\HttpClient; |
11
|
|
|
use Psr\Http\Client\ClientInterface; |
12
|
|
|
use Psr\Http\Message\RequestInterface; |
13
|
|
|
use Psr\Http\Message\ResponseInterface; |
14
|
|
|
|
15
|
|
|
/** |
16
|
|
|
* A HttpClientPoolItem represent a HttpClient inside a Pool. |
17
|
|
|
* |
18
|
|
|
* It is disabled when a request failed and can be reenabled after a certain number of seconds. |
19
|
|
|
* It also keep tracks of the current number of open requests the client is currently being sending |
20
|
|
|
* (only usable for async method). |
21
|
|
|
* |
22
|
|
|
* This class is used internally in the client pools and is not supposed to be used anywhere else. |
23
|
|
|
* |
24
|
|
|
* @final |
25
|
|
|
* |
26
|
|
|
* @internal |
27
|
|
|
* |
28
|
|
|
* @author Joel Wurtz <[email protected]> |
29
|
|
|
*/ |
30
|
|
|
class HttpClientPoolItem implements HttpClient, HttpAsyncClient |
31
|
|
|
{ |
32
|
|
|
/** |
33
|
|
|
* @var int Number of request this client is currently sending |
34
|
|
|
*/ |
35
|
|
|
private $sendingRequestCount = 0; |
36
|
|
|
|
37
|
|
|
/** |
38
|
|
|
* @var \DateTime|null Time when this client has been disabled or null if enable |
39
|
|
|
*/ |
40
|
|
|
private $disabledAt; |
41
|
|
|
|
42
|
|
|
/** |
43
|
|
|
* Number of seconds until this client is enabled again after an error. |
44
|
|
|
* |
45
|
|
|
* null: never reenable this client. |
46
|
|
|
* |
47
|
|
|
* @var int|null |
48
|
|
|
*/ |
49
|
|
|
private $reenableAfter; |
50
|
|
|
|
51
|
|
|
/** |
52
|
|
|
* @var FlexibleHttpClient A http client responding to async and sync request |
53
|
|
|
*/ |
54
|
|
|
private $client; |
55
|
|
|
|
56
|
|
|
/** |
57
|
|
|
* @param ClientInterface|HttpAsyncClient $client |
58
|
|
|
* @param int|null $reenableAfter Number of seconds until this client is enabled again after an error |
59
|
|
|
*/ |
60
|
23 |
View Code Duplication |
public function __construct($client, int $reenableAfter = null) |
|
|
|
|
61
|
|
|
{ |
62
|
23 |
|
if (!$client instanceof ClientInterface && !$client instanceof HttpAsyncClient) { |
63
|
|
|
throw new \TypeError( |
64
|
|
|
sprintf('%s::__construct(): Argument #1 ($client) must be of type %s|%s, %s given', self::class, ClientInterface::class, HttpAsyncClient::class, get_debug_type($client)) |
|
|
|
|
65
|
|
|
); |
66
|
|
|
} |
67
|
|
|
|
68
|
23 |
|
$this->client = new FlexibleHttpClient($client); |
69
|
23 |
|
$this->reenableAfter = $reenableAfter; |
70
|
23 |
|
} |
71
|
|
|
|
72
|
|
|
/** |
73
|
|
|
* {@inheritdoc} |
74
|
|
|
*/ |
75
|
13 |
|
public function sendRequest(RequestInterface $request): ResponseInterface |
76
|
|
|
{ |
77
|
13 |
|
if ($this->isDisabled()) { |
78
|
1 |
|
throw new Exception\RequestException('Cannot send the request as this client has been disabled', $request); |
79
|
|
|
} |
80
|
|
|
|
81
|
|
|
try { |
82
|
13 |
|
$this->incrementRequestCount(); |
83
|
13 |
|
$response = $this->client->sendRequest($request); |
84
|
5 |
|
$this->decrementRequestCount(); |
85
|
8 |
|
} catch (Exception $e) { |
86
|
8 |
|
$this->disable(); |
87
|
8 |
|
$this->decrementRequestCount(); |
88
|
|
|
|
89
|
8 |
|
throw $e; |
90
|
|
|
} |
91
|
|
|
|
92
|
5 |
|
return $response; |
93
|
|
|
} |
94
|
|
|
|
95
|
|
|
/** |
96
|
|
|
* {@inheritdoc} |
97
|
|
|
*/ |
98
|
8 |
|
public function sendAsyncRequest(RequestInterface $request) |
99
|
|
|
{ |
100
|
8 |
|
if ($this->isDisabled()) { |
101
|
1 |
|
throw new Exception\RequestException('Cannot send the request as this client has been disabled', $request); |
102
|
|
|
} |
103
|
|
|
|
104
|
8 |
|
$this->incrementRequestCount(); |
105
|
|
|
|
106
|
|
|
return $this->client->sendAsyncRequest($request)->then(function ($response) { |
107
|
1 |
|
$this->decrementRequestCount(); |
108
|
|
|
|
109
|
1 |
|
return $response; |
110
|
|
|
}, function ($exception) { |
111
|
2 |
|
$this->disable(); |
112
|
2 |
|
$this->decrementRequestCount(); |
113
|
|
|
|
114
|
2 |
|
throw $exception; |
115
|
8 |
|
}); |
116
|
|
|
} |
117
|
|
|
|
118
|
|
|
/** |
119
|
|
|
* Whether this client is disabled or not. |
120
|
|
|
* |
121
|
|
|
* If the client was disabled, calling this method checks if the client can |
122
|
|
|
* be reenabled and if so enables it. |
123
|
|
|
*/ |
124
|
21 |
|
public function isDisabled(): bool |
125
|
|
|
{ |
126
|
21 |
|
if (null !== $this->reenableAfter && null !== $this->disabledAt) { |
127
|
|
|
// Reenable after a certain time |
128
|
5 |
|
$now = new \DateTime(); |
129
|
|
|
|
130
|
5 |
|
if (($now->getTimestamp() - $this->disabledAt->getTimestamp()) >= $this->reenableAfter) { |
131
|
5 |
|
$this->enable(); |
132
|
|
|
|
133
|
5 |
|
return false; |
134
|
|
|
} |
135
|
|
|
|
136
|
|
|
return true; |
137
|
|
|
} |
138
|
|
|
|
139
|
21 |
|
return null !== $this->disabledAt; |
140
|
|
|
} |
141
|
|
|
|
142
|
|
|
/** |
143
|
|
|
* Get current number of request that are currently being sent by the underlying HTTP client. |
144
|
|
|
*/ |
145
|
2 |
|
public function getSendingRequestCount(): int |
146
|
|
|
{ |
147
|
2 |
|
return $this->sendingRequestCount; |
148
|
|
|
} |
149
|
|
|
|
150
|
|
|
/** |
151
|
|
|
* Increment the request count. |
152
|
|
|
*/ |
153
|
21 |
|
private function incrementRequestCount(): void |
154
|
|
|
{ |
155
|
21 |
|
++$this->sendingRequestCount; |
156
|
21 |
|
} |
157
|
|
|
|
158
|
|
|
/** |
159
|
|
|
* Decrement the request count. |
160
|
|
|
*/ |
161
|
16 |
|
private function decrementRequestCount(): void |
162
|
|
|
{ |
163
|
16 |
|
--$this->sendingRequestCount; |
164
|
16 |
|
} |
165
|
|
|
|
166
|
|
|
/** |
167
|
|
|
* Enable the current client. |
168
|
|
|
*/ |
169
|
5 |
|
private function enable(): void |
170
|
|
|
{ |
171
|
5 |
|
$this->disabledAt = null; |
172
|
5 |
|
} |
173
|
|
|
|
174
|
|
|
/** |
175
|
|
|
* Disable the current client. |
176
|
|
|
*/ |
177
|
10 |
|
private function disable(): void |
178
|
|
|
{ |
179
|
10 |
|
$this->disabledAt = new \DateTime('now'); |
180
|
10 |
|
} |
181
|
|
|
} |
182
|
|
|
|
Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.
You can also find more detailed suggestions in the “Code” section of your repository.