Skip to content

Commit b0c4f8e

Browse files
Tweaks (#216)
* Tweaks * Resolve comments * Delegate timeout to HttpClient Co-authored-by: Jérémy Derussé <jeremy@derusse.com>
1 parent ef02a29 commit b0c4f8e

File tree

7 files changed

+114
-63
lines changed

7 files changed

+114
-63
lines changed

Credentials/InstanceProvider.php

Lines changed: 11 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
use Psr\Log\LoggerInterface;
99
use Psr\Log\NullLogger;
1010
use Symfony\Component\HttpClient\HttpClient;
11+
use Symfony\Contracts\HttpClient\Exception\DecodingExceptionInterface;
1112
use Symfony\Contracts\HttpClient\Exception\HttpExceptionInterface;
1213
use Symfony\Contracts\HttpClient\Exception\TransportExceptionInterface;
1314
use Symfony\Contracts\HttpClient\HttpClientInterface;
@@ -27,17 +28,20 @@ class InstanceProvider implements CredentialProvider
2728

2829
private $httpClient;
2930

30-
public function __construct(?HttpClientInterface $httpClient = null, ?LoggerInterface $logger = null)
31+
private $timeout;
32+
33+
public function __construct(?HttpClientInterface $httpClient = null, ?LoggerInterface $logger = null, float $timeout = 1.0)
3134
{
3235
$this->logger = $logger ?? new NullLogger();
3336
$this->httpClient = $httpClient ?? HttpClient::create();
37+
$this->timeout = $timeout;
3438
}
3539

3640
public function getCredentials(Configuration $configuration): ?Credentials
3741
{
3842
// fetch current Profile
3943
try {
40-
$response = $this->httpClient->request('GET', self::ENDPOINT, ['timeout' => 1.0]);
44+
$response = $this->httpClient->request('GET', self::ENDPOINT, ['timeout' => $this->timeout]);
4145
$profile = $response->getContent();
4246
} catch (TransportExceptionInterface $e) {
4347
$this->logger->info('Failed to fetch Profile from Instance Metadata.', ['exception' => $e]);
@@ -52,17 +56,16 @@ public function getCredentials(Configuration $configuration): ?Credentials
5256
// fetch credentials from profile
5357
try {
5458
$response = $this->httpClient->request('GET', self::ENDPOINT . '/' . $profile, ['timeout' => 1.0]);
55-
$result = \json_decode($response->getContent(), true);
56-
if (\json_last_error() > 0) {
57-
$this->logger->info('Failed to decode Credentials.', ['error' => \json_last_error_msg()]);
58-
59-
return null;
60-
}
59+
$result = $response->toArray();
6160
if ('Success' !== $result['Code']) {
6261
$this->logger->info('Unexpected instance profile.', ['response_code' => $result['Code']]);
6362

6463
return null;
6564
}
65+
} catch (DecodingExceptionInterface $e) {
66+
$this->logger->info('Failed to decode Credentials.', ['exception' => $e]);
67+
68+
return null;
6669
} catch (TransportExceptionInterface $e) {
6770
$this->logger->info('Failed to fetch Profile from Instance Metadata.', ['exception' => $e]);
6871

Result.php

Lines changed: 44 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
use AsyncAws\Core\Exception\Http\NetworkException;
1010
use AsyncAws\Core\Exception\Http\RedirectionException;
1111
use AsyncAws\Core\Exception\Http\ServerException;
12+
use AsyncAws\Core\Exception\RuntimeException;
1213
use Symfony\Contracts\HttpClient\Exception\TransportExceptionInterface;
1314
use Symfony\Contracts\HttpClient\HttpClientInterface;
1415
use Symfony\Contracts\HttpClient\ResponseInterface;
@@ -42,17 +43,16 @@ class Result
4243
private $prefetchResults = [];
4344

4445
/**
45-
* @var HttpClientInterface
46+
* @var HttpClientInterface|null
4647
*/
4748
private $httpClient;
4849

4950
/**
50-
* A Result can be resolved many times. This boolean is true if the result
51-
* has been resolved at least once.
51+
* A Result can be resolved many times. This variable contains the last resolve result.
5252
*
53-
* @var bool
53+
* @var bool|NetworkException|HttpException|null
5454
*/
55-
private $isResolved = false;
55+
private $resolveResult;
5656

5757
public function __construct(ResponseInterface $response, HttpClientInterface $httpClient, AbstractApi $awsClient = null, $request = null)
5858
{
@@ -68,7 +68,7 @@ public function __destruct()
6868
array_shift($this->prefetchResponses)->cancel();
6969
}
7070

71-
if (false === $this->isResolved) {
71+
if (null === $this->resolveResult) {
7272
$this->resolve();
7373
}
7474
}
@@ -85,36 +85,50 @@ public function __destruct()
8585
*/
8686
final public function resolve(?float $timeout = null): bool
8787
{
88-
if (!isset($this->response)) {
88+
if (null !== $this->resolveResult) {
89+
if ($this->resolveResult instanceof \Exception) {
90+
throw $this->resolveResult;
91+
}
92+
93+
if (\is_bool($this->resolveResult)) {
94+
return $this->resolveResult;
95+
}
96+
97+
throw new RuntimeException('Unexpected resolve state');
98+
}
99+
100+
if (null === $this->response || null === $this->httpClient) {
89101
return true;
90102
}
91103

92104
try {
93-
if (null !== $timeout && $this->httpClient->stream($this->response, $timeout)->current()->isTimeout()) {
94-
return false;
105+
foreach ($this->httpClient->stream($this->response, $timeout) as $chunk) {
106+
if ($chunk->isTimeout()) {
107+
return false;
108+
}
109+
if ($chunk->isFirst()) {
110+
break;
111+
}
95112
}
113+
96114
$statusCode = $this->response->getStatusCode();
97115
} catch (TransportExceptionInterface $e) {
98-
// When a network error occurs
99-
$this->isResolved = true;
100-
101-
throw new NetworkException('Could not contact remote server.', 0, $e);
116+
throw $this->resolveResult = new NetworkException('Could not contact remote server.', 0, $e);
102117
}
103118

104-
$this->isResolved = true;
105119
if (500 <= $statusCode) {
106-
throw new ServerException($this->response);
120+
throw $this->resolveResult = new ServerException($this->response);
107121
}
108122

109123
if (400 <= $statusCode) {
110-
throw new ClientException($this->response);
124+
throw $this->resolveResult = new ClientException($this->response);
111125
}
112126

113127
if (300 <= $statusCode) {
114-
throw new RedirectionException($this->response);
128+
throw $this->resolveResult = new RedirectionException($this->response);
115129
}
116130

117-
return true;
131+
return $this->resolveResult = true;
118132
}
119133

120134
/**
@@ -128,50 +142,50 @@ final public function resolve(?float $timeout = null): bool
128142
*/
129143
final public function info(): array
130144
{
131-
if (!isset($this->response)) {
145+
if (null === $this->response) {
132146
return [
133-
'resolved' => $this->isResolved,
147+
'resolved' => null !== $this->resolveResult,
134148
];
135149
}
136150

137151
return [
138-
'resolved' => $this->isResolved,
152+
'resolved' => null !== $this->resolveResult,
139153
'response' => $this->response,
140154
'status' => (int) $this->response->getInfo('http_code'),
141155
];
142156
}
143157

144158
final public function cancel(): void
145159
{
146-
if (!isset($this->response)) {
160+
if (null === $this->response) {
147161
return;
148162
}
149163

150164
$this->response->cancel();
151-
unset($this->response);
165+
$this->resolveResult = false;
166+
$this->response = null;
152167
}
153168

154169
final protected function registerPrefetch(self $result): void
155170
{
156-
$this->prefetchResults[spl_object_hash($result)] = $result;
171+
$this->prefetchResults[spl_object_id($result)] = $result;
157172
}
158173

159174
final protected function unregisterPrefetch(self $result): void
160175
{
161-
unset($this->prefetchResults[spl_object_hash($result)]);
176+
unset($this->prefetchResults[spl_object_id($result)]);
162177
}
163178

164179
final protected function initialize(): void
165180
{
166-
if (!isset($this->response)) {
181+
if (null === $this->response || null === $this->httpClient) {
167182
return;
168183
}
184+
169185
$this->resolve();
170186
$this->populateResult($this->response, $this->httpClient);
171-
unset($this->response);
172-
if (isset($this->httpClient)) {
173-
unset($this->httpClient);
174-
}
187+
$this->response = null;
188+
$this->httpClient = null;
175189
}
176190

177191
protected function populateResult(ResponseInterface $response, HttpClientInterface $httpClient): void

Test/Http/SimpleMockedResponse.php

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,9 +4,9 @@
44

55
namespace AsyncAws\Core\Test\Http;
66

7-
use Symfony\Contracts\HttpClient\ResponseInterface;
7+
use Symfony\Component\HttpClient\Response\MockResponse;
88

9-
class SimpleMockedResponse implements ResponseInterface
9+
class SimpleMockedResponse extends MockResponse
1010
{
1111
private $headers = [];
1212

@@ -28,6 +28,8 @@ public function __construct(string $content = '', array $headers = [], int $stat
2828
}
2929
$this->headers[$name] = $value;
3030
}
31+
32+
parent::__construct($content, ['response_headers' => $headers, 'http_code' => $statusCode]);
3133
}
3234

3335
public function getStatusCode(): int
@@ -47,7 +49,7 @@ public function getContent(bool $throw = true): string
4749

4850
public function toArray(bool $throw = true): array
4951
{
50-
throw new \LogicException('Not implemented');
52+
return \json_decode($this->getContent($throw), true);
5153
}
5254

5355
public function cancel(): void

Tests/Unit/Result/AssumeRoleResponseTest.php

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,8 @@ public function testAssumeRoleResponse(): void
3838
</AssumeRoleResponse>
3939
');
4040

41-
$result = new AssumeRoleResponse($response, new MockHttpClient());
41+
$client = new MockHttpClient($response);
42+
$result = new AssumeRoleResponse($client->request('POST', 'http://localhost'), $client);
4243

4344
self::assertSame('arn:aws:sts::123456789012:assumed-role/demo/TestAR', $result->getAssumedRoleUser()->getArn());
4445
self::assertSame('ARO123EXAMPLE123:TestAR', $result->getAssumedRoleUser()->getAssumedRoleId());

Tests/Unit/Result/GetCallerIdentityResponseTest.php

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,8 @@ public function testGetCallerIdentityResponse(): void
2525
</GetCallerIdentityResponse>
2626
');
2727

28-
$result = new GetCallerIdentityResponse($response, new MockHttpClient());
28+
$client = new MockHttpClient($response);
29+
$result = new GetCallerIdentityResponse($client->request('POST', 'http://localhost'), $client);
2930

3031
self::assertStringContainsString('ARO123EXAMPLE123:my-role-session-name', $result->getUserId());
3132
self::assertStringContainsString('123456789012', $result->getAccount());

Tests/Unit/ResultTest.php

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88
use AsyncAws\Core\Result;
99
use AsyncAws\Core\Test\Http\SimpleMockedResponse;
1010
use PHPUnit\Framework\TestCase;
11-
use Symfony\Component\HttpClient\HttpClient;
11+
use Symfony\Component\HttpClient\MockHttpClient;
1212

1313
class ResultTest extends TestCase
1414
{
@@ -54,17 +54,19 @@ public function testPopulateResultXml()
5454

5555
public function testThrowExceptionDestruct()
5656
{
57-
$httpClient = HttpClient::create();
58-
$result = new Result(new SimpleMockedResponse('Bad request', [], 400), $httpClient);
57+
$response = new SimpleMockedResponse('Bad request', [], 400);
58+
$client = new MockHttpClient($response);
59+
$result = new Result($client->request('POST', 'http://localhost'), $client);
5960

6061
$this->expectException(ClientException::class);
6162
unset($result);
6263
}
6364

6465
public function testThrowExceptionOnlyOnce()
6566
{
66-
$httpClient = HttpClient::create();
67-
$result = new Result(new SimpleMockedResponse('Bad request', [], 400), $httpClient);
67+
$response = new SimpleMockedResponse('Bad request', [], 400);
68+
$client = new MockHttpClient($response);
69+
$result = new Result($client->request('POST', 'http://localhost'), $client);
6870

6971
try {
7072
$result->resolve();

0 commit comments

Comments
 (0)