Skip to content

Commit 48e890d

Browse files
authored
Fix exception thrown twice in waiter (#928)
1 parent 7cab23c commit 48e890d

File tree

2 files changed

+109
-4
lines changed

2 files changed

+109
-4
lines changed

src/Waiter.php

Lines changed: 26 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,11 @@ class Waiter
4949
*/
5050
private $finalState;
5151

52+
/**
53+
* @var bool
54+
*/
55+
private $resolved = false;
56+
5257
public function __construct(Response $response, AbstractApi $awsClient, $request)
5358
{
5459
$this->response = $response;
@@ -58,7 +63,9 @@ public function __construct(Response $response, AbstractApi $awsClient, $request
5863

5964
public function __destruct()
6065
{
61-
$this->resolve();
66+
if (!$this->resolved) {
67+
$this->resolve();
68+
}
6269
}
6370

6471
final public function isSuccess(): bool
@@ -91,10 +98,12 @@ final public function getState(): string
9198
$exception = null;
9299
} catch (HttpException $exception) {
93100
// use $exception later
101+
} finally {
102+
$this->resolved = true;
103+
$this->needRefresh = true;
94104
}
95105

96106
$state = $this->extractState($this->response, $exception);
97-
$this->needRefresh = true;
98107

99108
switch ($state) {
100109
case self::STATE_SUCCESS:
@@ -126,6 +135,8 @@ final public function resolve(?float $timeout = null): bool
126135
return $this->response->resolve($timeout);
127136
} catch (HttpException $exception) {
128137
return true;
138+
} finally {
139+
$this->resolved = true;
129140
}
130141
}
131142

@@ -148,6 +159,7 @@ final public function cancel(): void
148159
{
149160
$this->response->cancel();
150161
$this->needRefresh = true;
162+
$this->resolved = true;
151163
}
152164

153165
/**
@@ -161,18 +173,27 @@ final public function cancel(): void
161173
*/
162174
final public function wait(float $timeout = null, float $delay = null): bool
163175
{
176+
if (null !== $this->finalState) {
177+
return true;
178+
}
179+
164180
$timeout = $timeout ?? static::WAIT_TIMEOUT;
165181
$delay = $delay ?? static::WAIT_DELAY;
166182

167183
$start = \microtime(true);
168184
while (true) {
185+
if ($this->needRefresh) {
186+
$this->stealResponse($this->refreshState());
187+
}
188+
169189
// If request times out
170190
if (!$this->resolve($timeout - (\microtime(true) - $start))) {
171191
break;
172192
}
173193

194+
$this->getState();
174195
// If we reached a final state
175-
if (\in_array($this->getState(), [self::STATE_SUCCESS, self::STATE_FAILURE])) {
196+
if ($this->finalState) {
176197
return true;
177198
}
178199

@@ -182,7 +203,6 @@ final public function wait(float $timeout = null, float $delay = null): bool
182203
}
183204

184205
\usleep((int) ceil($delay * 1000000));
185-
$this->stealResponse($this->refreshState());
186206
}
187207

188208
return false;
@@ -201,6 +221,8 @@ protected function refreshState(): Waiter
201221
private function stealResponse(self $waiter): void
202222
{
203223
$this->response = $waiter->response;
224+
$this->resolved = $waiter->resolved;
225+
$waiter->resolved = true;
204226
$this->needRefresh = false;
205227
}
206228
}

tests/Integration/WaiterTest.php

Lines changed: 83 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,83 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace AsyncAws\Core\Tests\Integration;
6+
7+
use AsyncAws\Core\Exception\Http\NetworkException;
8+
use AsyncAws\S3\S3Client;
9+
use PHPUnit\Framework\TestCase;
10+
use Symfony\Component\HttpClient\HttpClient;
11+
12+
class WaiterTest extends TestCase
13+
{
14+
public function testWaiterException(): void
15+
{
16+
if (!\class_exists(S3Client::class)) {
17+
self::markTestSkipped('This test needs a client with waiter endpoints');
18+
}
19+
20+
$client = new S3Client(['endpoint' => 'http://127.0.0.1:10'], null, HttpClient::create(['timeout' => 5]));
21+
$result = $client->bucketExists(['Bucket' => 'test']);
22+
23+
$this->expectException(NetworkException::class);
24+
$result->isSuccess();
25+
}
26+
27+
public function testWaiterThrowOnce(): void
28+
{
29+
if (!\class_exists(S3Client::class)) {
30+
self::markTestSkipped('This test needs a client with waiter endpoints');
31+
}
32+
33+
$client = new S3Client(['endpoint' => 'http://127.0.0.1:10'], null, HttpClient::create(['timeout' => 5]));
34+
$result = $client->bucketExists(['Bucket' => 'test']);
35+
36+
try {
37+
$result->isSuccess();
38+
self::fail('An exception should have been triggered');
39+
} catch (NetworkException $e) {
40+
}
41+
42+
unset($result);
43+
self::expectNotToPerformAssertions();
44+
}
45+
46+
public function testWaiterThrowOnRetry(): void
47+
{
48+
if (!\class_exists(S3Client::class)) {
49+
self::markTestSkipped('This test needs a client with waiter endpoints');
50+
}
51+
52+
$client = new S3Client(['endpoint' => 'http://127.0.0.1:10'], null, HttpClient::create(['timeout' => 5]));
53+
$result = $client->bucketExists(['Bucket' => 'test']);
54+
55+
try {
56+
$result->isSuccess();
57+
self::fail('An exception should have been triggered');
58+
} catch (NetworkException $e) {
59+
}
60+
61+
$this->expectException(NetworkException::class);
62+
$result->isSuccess();
63+
}
64+
65+
public function testWaiterThrowOnWait(): void
66+
{
67+
if (!\class_exists(S3Client::class)) {
68+
self::markTestSkipped('This test needs a client with waiter endpoints');
69+
}
70+
71+
$client = new S3Client(['endpoint' => 'http://127.0.0.1:10'], null, HttpClient::create(['timeout' => 5]));
72+
$result = $client->bucketExists(['Bucket' => 'test']);
73+
74+
try {
75+
$result->isSuccess();
76+
self::fail('An exception should have been triggered');
77+
} catch (NetworkException $e) {
78+
}
79+
80+
$this->expectException(NetworkException::class);
81+
$result->wait();
82+
}
83+
}

0 commit comments

Comments
 (0)