OXIESEC PANEL
- Current Dir:
/
/
var
/
www
/
email
/
vendor
/
aws
/
aws-sdk-php
/
tests
Server IP: 139.59.38.164
Upload:
Create Dir:
Name
Size
Modified
Perms
📁
..
-
07/10/2024 05:23:06 AM
rwxr-xr-x
📄
AbstractConfigurationProviderTest.php
3.42 KB
07/10/2024 05:22:21 AM
rw-r--r--
📁
Api
-
07/10/2024 05:37:54 AM
rwxr-xr-x
📁
Arn
-
07/10/2024 05:27:54 AM
rwxr-xr-x
📁
Auth
-
07/10/2024 05:24:30 AM
rwxr-xr-x
📄
AwsClientTest.php
26.16 KB
07/10/2024 05:22:21 AM
rw-r--r--
📁
Build
-
07/10/2024 05:27:55 AM
rwxr-xr-x
📄
ClientResolverTest.php
55.74 KB
07/10/2024 05:22:21 AM
rw-r--r--
📁
ClientSideMonitoring
-
07/10/2024 05:24:31 AM
rwxr-xr-x
📁
CloudFront
-
07/10/2024 05:27:55 AM
rwxr-xr-x
📁
CloudSearchDomain
-
07/10/2024 05:24:33 AM
rwxr-xr-x
📁
CloudTrail
-
07/10/2024 05:24:33 AM
rwxr-xr-x
📁
CloudWatchLogs
-
07/10/2024 05:24:33 AM
rwxr-xr-x
📁
CognitoIdentity
-
07/10/2024 05:24:33 AM
rwxr-xr-x
📁
CognitoSync
-
07/10/2024 05:24:34 AM
rwxr-xr-x
📄
CommandPoolTest.php
5.8 KB
07/10/2024 05:22:21 AM
rw-r--r--
📄
CommandTest.php
3.28 KB
07/10/2024 05:22:21 AM
rw-r--r--
📄
ConfigurationResolverTest.php
12.35 KB
07/10/2024 05:22:21 AM
rw-r--r--
📁
Credentials
-
07/10/2024 05:37:43 AM
rwxr-xr-x
📁
Crypto
-
07/10/2024 05:27:55 AM
rwxr-xr-x
📁
DefaultsMode
-
07/10/2024 05:24:37 AM
rwxr-xr-x
📁
DocDb
-
07/10/2024 05:24:37 AM
rwxr-xr-x
📄
DoctrineCacheAdapterTest.php
1.23 KB
07/10/2024 05:22:21 AM
rw-r--r--
📁
DynamoDb
-
07/10/2024 05:24:38 AM
rwxr-xr-x
📁
Ec2
-
07/10/2024 05:24:38 AM
rwxr-xr-x
📁
ElasticLoadBalancingV2
-
07/10/2024 05:24:38 AM
rwxr-xr-x
📁
Endpoint
-
07/10/2024 05:27:56 AM
rwxr-xr-x
📁
EndpointDiscovery
-
07/10/2024 05:37:55 AM
rwxr-xr-x
📄
EndpointParameterMiddlewareTest.php
9.21 KB
07/10/2024 05:22:21 AM
rw-r--r--
📁
EndpointV2
-
07/10/2024 05:27:58 AM
rwxr-xr-x
📁
EventBridge
-
07/10/2024 05:24:41 AM
rwxr-xr-x
📁
Exception
-
07/10/2024 05:24:42 AM
rwxr-xr-x
📄
FunctionsTest.php
14.33 KB
07/10/2024 05:22:21 AM
rw-r--r--
📁
Glacier
-
07/10/2024 05:24:42 AM
rwxr-xr-x
📁
Handler
-
07/10/2024 05:28:00 AM
rwxr-xr-x
📄
HandlerListTest.php
7.16 KB
07/10/2024 05:22:22 AM
rw-r--r--
📄
HasMonitoringEventsTraitTest.php
895 bytes
07/10/2024 05:22:22 AM
rw-r--r--
📄
HashingStreamTest.php
1.41 KB
07/10/2024 05:22:22 AM
rw-r--r--
📄
HistoryTest.php
4.2 KB
07/10/2024 05:22:22 AM
rw-r--r--
📄
IdempotencyTokenMiddlewareTest.php
2.76 KB
07/10/2024 05:22:22 AM
rw-r--r--
📁
Identity
-
07/10/2024 05:28:00 AM
rwxr-xr-x
📄
InputValidationMiddlewareTest.php
5.62 KB
07/10/2024 05:22:22 AM
rw-r--r--
📁
Integ
-
07/10/2024 05:24:44 AM
rwxr-xr-x
📄
JsonCompilerTest.php
730 bytes
07/10/2024 05:22:22 AM
rw-r--r--
📁
Lambda
-
07/10/2024 05:24:44 AM
rwxr-xr-x
📁
LexModelsV2
-
07/10/2024 05:24:44 AM
rwxr-xr-x
📄
LruArrayCacheTest.php
1.59 KB
07/10/2024 05:22:22 AM
rw-r--r--
📁
MachineLearning
-
07/10/2024 05:24:44 AM
rwxr-xr-x
📄
MiddlewareTest.php
16.08 KB
07/10/2024 05:22:22 AM
rw-r--r--
📄
MockHandlerTest.php
3.79 KB
07/10/2024 05:22:22 AM
rw-r--r--
📄
MultiRegionClientTest.php
5.41 KB
07/10/2024 05:22:23 AM
rw-r--r--
📁
Multipart
-
07/10/2024 05:24:45 AM
rwxr-xr-x
📁
Neptune
-
07/10/2024 05:24:45 AM
rwxr-xr-x
📄
PerformanceContext.php
8.2 KB
07/10/2024 05:22:23 AM
rw-r--r--
📄
PhpHashTest.php
1.33 KB
07/10/2024 05:22:23 AM
rw-r--r--
📁
Polly
-
07/10/2024 05:24:45 AM
rwxr-xr-x
📄
PresignUrlMiddlewareTest.php
3.95 KB
07/10/2024 05:22:23 AM
rw-r--r--
📄
Psr16CacheAdapterTest.php
1.84 KB
07/10/2024 05:22:23 AM
rw-r--r--
📄
PsrCacheAdapterTest.php
2.64 KB
07/10/2024 05:22:23 AM
rw-r--r--
📄
QueryCompatibleInputMiddlewareTest.php
9.1 KB
07/10/2024 05:22:23 AM
rw-r--r--
📁
Rds
-
07/10/2024 05:24:46 AM
rwxr-xr-x
📁
RequestCompression
-
07/10/2024 05:28:00 AM
rwxr-xr-x
📄
ResultPaginatorTest.php
16.21 KB
07/10/2024 05:22:23 AM
rw-r--r--
📄
ResultTest.php
1.38 KB
07/10/2024 05:22:23 AM
rw-r--r--
📁
Retry
-
07/10/2024 05:24:47 AM
rwxr-xr-x
📄
RetryMiddlewareTest.php
24.57 KB
07/10/2024 05:22:23 AM
rw-r--r--
📄
RetryMiddlewareV2Test.php
40.33 KB
07/10/2024 05:22:24 AM
rw-r--r--
📁
Route53
-
07/10/2024 05:24:47 AM
rwxr-xr-x
📁
S3
-
07/10/2024 05:37:56 AM
rwxr-xr-x
📁
S3Control
-
07/10/2024 05:24:51 AM
rwxr-xr-x
📁
Script
-
07/10/2024 05:24:51 AM
rwxr-xr-x
📄
SdkTest.php
2.37 KB
07/10/2024 05:22:24 AM
rw-r--r--
📁
Ses
-
07/10/2024 05:24:51 AM
rwxr-xr-x
📁
Signature
-
07/10/2024 05:24:52 AM
rwxr-xr-x
📁
Sqs
-
07/10/2024 05:24:52 AM
rwxr-xr-x
📁
Ssm
-
07/10/2024 05:24:52 AM
rwxr-xr-x
📄
StreamRequestPayloadMiddlewareTest.php
10.1 KB
07/10/2024 05:22:24 AM
rw-r--r--
📁
Sts
-
07/10/2024 05:28:04 AM
rwxr-xr-x
📄
TestServiceTrait.php
3.78 KB
07/10/2024 05:22:24 AM
rw-r--r--
📁
Token
-
07/10/2024 05:24:54 AM
rwxr-xr-x
📄
TraceMiddlewareTest.php
16.16 KB
07/10/2024 05:22:24 AM
rw-r--r--
📄
UsesServiceTrait.php
4.37 KB
07/10/2024 05:22:24 AM
rw-r--r--
📄
WaiterTest.php
12.63 KB
07/10/2024 05:22:25 AM
rw-r--r--
📄
WrappedHttpHandlerTest.php
13.8 KB
07/10/2024 05:22:25 AM
rw-r--r--
📁
bootstrap
-
07/10/2024 05:24:30 AM
rwxr-xr-x
📄
bootstrap.php
1.53 KB
07/10/2024 05:22:21 AM
rw-r--r--
📁
fixtures
-
07/10/2024 05:37:56 AM
rwxr-xr-x
📁
static
-
07/10/2024 05:24:53 AM
rwxr-xr-x
Editing: RetryMiddlewareV2Test.php
Close
<?php namespace Aws\Test; use Aws\Api\ApiProvider; use Aws\Api\Service; use Aws\Command; use Aws\CommandInterface; use Aws\Exception\AwsException; use Aws\MockHandler; use Aws\Result; use Aws\ResultInterface; use Aws\Retry\Configuration; use Aws\Retry\QuotaManager; use Aws\Retry\RateLimiter; use Aws\RetryMiddlewareV2; use GuzzleHttp\Exception\RequestException; use GuzzleHttp\Promise\RejectedPromise; use GuzzleHttp\Psr7\Request; use GuzzleHttp\Psr7\Response; use Psr\Http\Message\RequestInterface; use Yoast\PHPUnitPolyfills\TestCases\TestCase; /** * @covers \Aws\RetryMiddlewareV2 */ class RetryMiddlewareV2Test extends TestCase { use UsesServiceTrait; /** * @dataProvider standardModeTestCases * * @param CommandInterface $command * @param QuotaManager $quotaManager * @param array $queue * @param array $options * @param $expected * @throws \Exception */ public function testRetriesForStandardMode( CommandInterface $command, QuotaManager $quotaManager, array $queue, array $options, $expected ) { $request = new Request('GET', 'http://www.example.com'); $attempt = 0; // Errors within MockHandler closure get caught silently $errors = []; $mock = new MockHandler( $queue, function() use ($expected, &$attempt, $quotaManager, &$errors, $command) { try { $this->assertEquals( $expected[$attempt]['quota'], $quotaManager->getAvailableCapacity() ); if (!empty($expected[$attempt]['max_delay'])) { $this->assertLessThanOrEqual( $expected[$attempt]['max_delay'], $command['@http']['delay'] ); } } catch (\Exception $e) { // Catch errors manually for throwing later $errors[] = $e; } $attempt++; }, function() use ($expected, &$attempt, $quotaManager, &$errors, $command) { try { $this->assertEquals( $expected[$attempt]['quota'], $quotaManager->getAvailableCapacity() ); if (!empty($expected[$attempt]['max_delay'])) { $this->assertLessThanOrEqual( $expected[$attempt]['max_delay'], $command['@http']['delay'] ); } } catch (\Exception $e) { // Catch errors manually for throwing later $errors[] = $e; } $attempt++; } ); $wrapped = new RetryMiddlewareV2( new Configuration('standard', $options['max_attempts']), $mock, [ 'decider' => RetryMiddlewareV2::createDefaultDecider( $quotaManager, $options['max_attempts'] ), 'max_backoff' => $options['max_backoff'] ] ); try { $wrapped($command, $request)->wait(); if (!empty($expected[$attempt - 1]['error'])) { $this->fail('This should have thrown an exception.'); } } catch (\Exception $e) { if (!empty($expected[$attempt - 1]['error'])) { $this->assertEquals( $expected[$attempt - 1]['error']->getMessage(), $e->getMessage() ); $this->assertInstanceOf( get_class($expected[$attempt - 1]['error']), $e ); } else { throw $e; } } // Throw first silently caught error if any if (!empty($errors)) { throw $errors[0]; } $this->assertCount($attempt, $queue); } function standardModeTestCases() { $command = new Command('foo'); $result200 = new Result([ '@metadata' => [ 'statusCode' => 200 ] ]); $awsException500 = new AwsException( 'Internal server error', $command, [ 'response' => new Response(500) ] ); $awsException502 = new AwsException( 'Bad gateway', $command, [ 'response' => new Response(502) ] ); return [ // Retry eventually succeeds [ $command, new QuotaManager([ 'initial_retry_tokens' => 500 ]), [ $awsException500, $awsException500, $result200 ], [ 'max_attempts' => 3, 'max_backoff' => 20000, ], [ [ 'quota' => 500 ], [ 'quota' => 495, 'max_delay' => 2000 ], [ 'quota' => 490, 'max_delay' => 4000 ] ] ], // Fail due to max attempts reached [ $command, new QuotaManager([ 'initial_retry_tokens' => 500 ]), [$awsException502, $awsException502, $awsException502], [ 'max_attempts' => 3, 'max_backoff' => 20000, ], [ [ 'quota' => 500 ], [ 'quota' => 495, 'max_delay' => 2000 ], [ 'quota' => 490, 'max_delay' => 4000, 'error' => $awsException502 ] ] ], // Retry quota reached after a single retry [ $command, new QuotaManager([ 'initial_retry_tokens' => 5 ]), [$awsException500, $awsException502], [ 'max_attempts' => 3, 'max_backoff' => 20000, ], [ [ 'quota' => 5 ], [ 'quota' => 0, 'max_delay' => 2000, 'error' => $awsException502 ], ] ], // No retry at all if quota is 0 [ $command, new QuotaManager([ 'initial_retry_tokens' => 0 ]), [$awsException500], [ 'max_attempts' => 3, 'max_backoff' => 20000, ], [ [ 'quota' => 0, 'max_delay' => 2000, 'error' => $awsException500 ], ] ], // Verify exponential backoff timing [ $command, new QuotaManager([ 'initial_retry_tokens' => 500 ]), [ $awsException500, $awsException500, $awsException500, $awsException500, $awsException500 ], [ 'max_attempts' => 5, 'max_backoff' => 20000, ], [ [ 'quota' => 500 ], [ 'quota' => 495, 'max_delay' => 2000 ], [ 'quota' => 490, 'max_delay' => 4000 ], [ 'quota' => 485, 'max_delay' => 8000 ], [ 'quota' => 480, 'max_delay' => 16000, 'error' => $awsException500 ], ], ], // Verify max backoff [ $command, new QuotaManager([ 'initial_retry_tokens' => 500 ]), [ $awsException500, $awsException500, $awsException500, $awsException500, $awsException500 ], [ 'max_attempts' => 5, 'max_backoff' => 3000, ], [ [ 'quota' => 500 ], [ 'quota' => 495, 'max_delay' => 2000 ], [ 'quota' => 490, 'max_delay' => 3000 ], [ 'quota' => 485, 'max_delay' => 3000 ], [ 'quota' => 480, 'max_delay' => 3000, 'error' => $awsException500 ], ], ] ]; } public function testRetriesForAdapativeMode() { $command = new Command('foo'); $request = new Request('GET', 'http://www.example.com'); $result200 = new Result([ '@metadata' => [ 'statusCode' => 200 ] ]); $nonThrottlingException = new AwsException( 'Internal server error', $command, [ 'response' => new Response(500), 'code' => 'SomeException', ] ); $throttlingException = new AwsException( 'ThrottlingException', $command, [ 'response' => new Response(502), 'code' => 'ThrottlingException', ] ); $customThrottlingException = new AwsException( 'CustomThrottlingException', $command, [ 'response' => new Response(502), 'code' => 'CustomThrottlingException', ] ); $provider = ApiProvider::filesystem(__DIR__ . '/fixtures/aws_exception_test'); $definition = $provider('api', 'ec2', 'latest'); $service = new Service($definition, $provider); $shapes = $service->getErrorShapes(); $errorShape = null; foreach ($shapes as $shape) { $definition = $shape->toArray(); if (!empty($definition['retryable']['throttling'])) { $errorShape = $shape; break; } } $throttlingErrorShapeException = new AwsException( 'ThrottlingErrorShape', $command, [ 'response' => new Response(400), 'code' => 'ThrottlingErrorShape', 'error_shape' => $errorShape, ] ); $time = microtime(true); $attempt = 0; $expectedTimes = [0, 0, 0, 1.9, 3.8, 5.7]; // Errors within MockHandler closure get caught silently $errors = []; $assertFunction = function() use (&$time, &$attempt, &$errors, $expectedTimes) { try { $this->assertLessThanOrEqual( 0.5, abs($expectedTimes[$attempt] - (microtime(true) - $time)) ); } catch (\Exception $e) { $errors[] = $e; } $time = microtime(true); $attempt++; }; $mock = new MockHandler( [ $nonThrottlingException, $nonThrottlingException, $throttlingException, $customThrottlingException, $throttlingErrorShapeException, $result200, ], $assertFunction, $assertFunction ); $times = [0, 0]; foreach ($expectedTimes as $index => $expected) { for ($i = 0; $i < 5; $i++) { $times[] = 0.1 * ($index + 1); } } $wrapped = new RetryMiddlewareV2( new Configuration('adaptive', 6), $mock, [ 'rate_limiter' => new RateLimiter([ 'time_provider' => function() use ($times) { static $i; if (is_null($i)) { $i = 0; } else { $i++; } return $times[$i]; } ]), 'throttling_error_codes' => ['CustomThrottlingException'] ] ); $wrapped($command, $request)->wait(); // Throw first silently caught error if any if (!empty($errors)) { throw $errors[0]; } $this->assertCount($attempt, $expectedTimes); } public function testAddRetryHeader() { $nextHandler = function (CommandInterface $command, RequestInterface $request) { $this->assertTrue($request->hasHeader('aws-sdk-retry')); return new RejectedPromise( new AwsException('e', $command, ['connection_error' => true]) ); }; $retryMW = new RetryMiddlewareV2( new Configuration( 'standard', 5 ), $nextHandler ); try { $retryMW(new Command('SomeCommand'), new Request('GET', ''))->wait(); $this->fail(); } catch (AwsException $e) { } } public function testDeciderRetriesWhenStatusCodeMatches() { $decider = RetryMiddlewareV2::createDefaultDecider(new QuotaManager()); $command = new Command('foo'); $result = new Result(['@metadata' => ['statusCode' => '500']]); $this->assertTrue($decider(0, $command, $result)); $result = new Result(['@metadata' => ['statusCode' => '503']]); $this->assertTrue($decider(0, $command, $result)); } public function testDeciderRetriesWhenConnectionError() { $decider = RetryMiddlewareV2::createDefaultDecider(new QuotaManager()); $command = new Command('foo'); $err = new AwsException('e', $command, ['connection_error' => true]); $this->assertTrue($decider(0, $command, $err)); $err = new AwsException('e', $command, ['connection_error' => false]); $this->assertFalse($decider(0, $command, $err)); } public function testDeciderIgnoresNonAwsExceptions() { $decider = RetryMiddlewareV2::createDefaultDecider(new QuotaManager()); $command = new Command('foo'); $err = new \Exception('e'); $this->assertFalse($decider(0, $command, $err)); } public function testDeciderIgnoresPHPError() { if (interface_exists('Throwable', false)) { $decider = RetryMiddlewareV2::createDefaultDecider(new QuotaManager()); $command = new Command('foo'); $request = new Request('GET', 'http://www.example.com'); $err = new \Error('e'); $this->assertFalse($decider(0, $command, $err)); } } public function testDeciderRetriesWhenCurlErrorCodeMatches() { if (!extension_loaded('curl')) { $this->markTestSkipped('Test skipped on no cURL extension'); } $decider = RetryMiddlewareV2::createDefaultDecider(new QuotaManager()); $command = new Command('foo'); $request = new Request('GET', 'http://www.example.com'); $version = \Aws\guzzle_major_version(); if ($version === 6 || $version === 7) { $previous = new RequestException( 'test', $request, null, null, ['errno' => CURLE_RECV_ERROR] ); } elseif ($version === 5) { $previous = new RequestException( 'cURL error ' . CURLE_RECV_ERROR . ': test', new \GuzzleHttp\Message\Request('GET', 'http://www.example.com') ); } $err = new AwsException( 'e', $command, ['connection_error' => false], $previous ); $this->assertTrue($decider(0, $command, $err)); } public function testDeciderRetriesForCustomCurlErrors() { if (!extension_loaded('curl')) { $this->markTestSkipped('Test skipped on no cURL extension'); } $decider = RetryMiddlewareV2::createDefaultDecider( new QuotaManager(), 3, ['curl_errors' => [CURLE_BAD_CONTENT_ENCODING]] ); $command = new Command('foo'); $request = new Request('GET', 'http://www.example.com'); $version = \Aws\guzzle_major_version(); // Custom error passed in to decider config should result in a retry if ($version === 6 || $version === 7) { $previous = new RequestException( 'test', $request, null, null, ['errno' => CURLE_BAD_CONTENT_ENCODING] ); } elseif ($version === 5) { $previous = new RequestException( 'cURL error ' . CURLE_BAD_CONTENT_ENCODING . ': test', new \GuzzleHttp\Message\Request('GET', 'http://www.example.com') ); } $err = new AwsException( 'e', $command, ['connection_error' => false], $previous ); $this->assertTrue($decider(0, $command, $err)); // Error not passed in to decider config should result in no retry if ($version === 6 || $version === 7) { $previous = new RequestException( 'test', $request, null, null, ['errno' => CURLE_ABORTED_BY_CALLBACK] ); } elseif ($version === 5) { $previous = new RequestException( 'cURL error ' . CURLE_ABORTED_BY_CALLBACK . ': test', new \GuzzleHttp\Message\Request('GET', 'http://www.example.com') ); } $err = new AwsException( 'e', $command, ['connection_error' => false], $previous ); $this->assertFalse($decider(0, $command, $err)); } public function awsErrorCodeProvider() { $command = new Command('foo'); return [ [new AwsException('e', $command, ['code' => 'RequestLimitExceeded'])], [new AwsException('e', $command, ['code' => 'Throttling'])], [new AwsException('e', $command, ['code' => 'ThrottlingException'])], [new AwsException('e', $command, ['code' => 'ProvisionedThroughputExceededException'])], [new AwsException('e', $command, ['code' => 'RequestThrottled'])], [new AwsException('e', $command, ['code' => 'BandwidthLimitExceeded'])], [new AwsException('e', $command, ['code' => 'RequestThrottledException'])], [new AwsException('e', $command, ['code' => 'TooManyRequestsException'])], [new AwsException('e', $command, ['code' => 'EC2ThrottledException'])], ]; } /** * @param $err * * @dataProvider awsErrorCodeProvider */ public function testDeciderRetriesWhenAwsErrorCodeMatches($err) { $decider = RetryMiddlewareV2::createDefaultDecider(new QuotaManager()); $command = new Command('foo'); $this->assertTrue($decider(0, $command, $err)); } public function testDeciderRetriesWhenExceptionStatusCodeMatches() { $decider = RetryMiddlewareV2::createDefaultDecider(new QuotaManager()); $command = new Command('foo'); $err = new AwsException('e', $command, ['response' => new Response(500)]); $this->assertTrue($decider(0, $command, $err)); $err = new AwsException('e', $command, ['response' => new Response(502)]); $this->assertTrue($decider(0, $command, $err)); $err = new AwsException('e', $command, ['response' => new Response(503)]); $this->assertTrue($decider(0, $command, $err)); $err = new AwsException('e', $command, ['response' => new Response(504)]); $this->assertTrue($decider(0, $command, $err)); $err = new AwsException('e', $command, ['response' => new Response(403)]); $this->assertFalse($decider(0, $command, $err)); } public function testDeciderRetriesForRetryableTrait() { $decider = RetryMiddlewareV2::createDefaultDecider(new QuotaManager()); $provider = ApiProvider::filesystem(__DIR__ . '/fixtures/aws_exception_test'); $definition = $provider('api', 'ec2', 'latest'); $service = new Service($definition, $provider); $shapes = $service->getErrorShapes(); $errorShape = null; foreach ($shapes as $shape) { $definition = $shape->toArray(); if (!empty($definition['retryable'])) { $errorShape = $shape; break; } } $command = new Command('foo'); $err = new AwsException( 'e', $command, [ 'response' => new Response(400), 'error_shape' => $errorShape ] ); $this->assertTrue($decider(0, $command, $err)); } public function testDeciderRetriesForCustomErrorCodes() { $decider = RetryMiddlewareV2::createDefaultDecider( new QuotaManager(), 3, [ 'transient_error_codes' => ['CustomRetryableException'], 'throttling_error_codes' => ['CustomThrottlingException'], ] ); $command = new Command('foo'); $err = new AwsException('e', $command, [ 'code' => 'CustomRetryableException' ]); $this->assertTrue($decider(0, $command, $err)); $err = new AwsException('e', $command, [ 'code' => 'CustomThrottlingException' ]); $this->assertTrue($decider(0, $command, $err)); $err = new AwsException('e', $command, [ 'code' => 'CustomNonRetryableException' ]); $this->assertFalse($decider(0, $command, $err)); } public function testDeciderRetriesForCustomStatusCodes() { $decider = RetryMiddlewareV2::createDefaultDecider( new QuotaManager(), 3, ['status_codes' => [400]] ); $command = new Command('foo'); $err = new AwsException('e', $command, ['response' => new Response(400)]); $this->assertTrue($decider(0, $command, $err)); $err = new AwsException('e', $command, ['response' => new Response(401)]); $this->assertFalse($decider(0, $command, $err)); } public function testDeciderDoesNotRetryAfterMaxAttempts() { $decider = RetryMiddlewareV2::createDefaultDecider(new QuotaManager()); $command = new Command('foo'); $err = new AwsException('e', $command, ['code' => 'RequestLimitExceeded']); $this->assertTrue($decider(0, $command, $err)); $this->assertFalse($decider(3, $command, $err)); } public function testUsesCustomDelayer() { $command = new Command('foo'); $request = new Request('GET', 'http://www.example.com'); $attempts = 0; $handler = function ($command, $request) use (&$attempts) { if ($attempts > 0) { $this->assertSame(9999, $command['@http']['delay']); } $attempts++; return \GuzzleHttp\Promise\Create::rejectionFor( new AwsException( 'foo', $command, [ 'response' => new Response(502) ] ) ); }; $wrapped = new RetryMiddlewareV2( new Configuration('standard', 3), $handler, [ 'decider' => RetryMiddlewareV2::createDefaultDecider(new QuotaManager()), 'delayer' => function () { return 9999; } ] ); try { $wrapped($command, $request)->wait(); $this->fail(); } catch (AwsException $e) { $this->assertSame(3, $attempts); $this->assertStringContainsString('foo', $e->getMessage()); } } public function testDelaysExponentiallyWithJitter() { $retryMiddleware = new RetryMiddlewareV2(new Configuration('standard', 3), function () {}); $this->assertLessThanOrEqual(2000, $retryMiddleware->exponentialDelayWithJitter(1)); $this->assertLessThanOrEqual(4000, $retryMiddleware->exponentialDelayWithJitter(2)); $this->assertLessThanOrEqual(8000, $retryMiddleware->exponentialDelayWithJitter(3)); $this->assertLessThanOrEqual(20000, $retryMiddleware->exponentialDelayWithJitter(10)); } public function testDelaysWithSomeRandomness() { $retryMiddleware = new RetryMiddlewareV2(new Configuration('standard', 3), function () {}); $maxDelay = 1000 * pow(2, 4); $values = array_map(function () use ($retryMiddleware) { return $retryMiddleware->exponentialDelayWithJitter(4); }, range(1, 200)); $this->assertGreaterThan(1, count(array_unique($values))); foreach ($values as $value) { $this->assertGreaterThanOrEqual(0, $value); $this->assertLessThanOrEqual($maxDelay, $value); } } public function testRetriesWhenResultMatches() { $command = new Command('foo'); $request = new Request('GET', 'http://www.example.com'); $res1 = new Result(['@metadata' => ['statusCode' => '503']]); $res2 = new Result(['@metadata' => ['statusCode' => '200']]); $mock = new MockHandler( [ function ($command, $request) use ($res1) { $this->assertArrayNotHasKey('delay', $command['@http']); return $res1; }, function ($command, $request) use ($res2) { $this->assertLessThanOrEqual(2000, $command['@http']['delay']); return $res2; }, ], function () use (&$called) { $called[] = func_get_args(); } ); $wrapped = new RetryMiddlewareV2( new Configuration('standard', 5), $mock, ['decider' => RetryMiddlewareV2::createDefaultDecider(new QuotaManager())] ); $result = $wrapped($command, $request)->wait(); $this->assertSame($res2, $result); $this->assertCount(2, $called); $this->assertSame([$res1], $called[0]); $this->assertSame([$res2], $called[1]); } public function testRetriesWhenExceptionMatches() { $command = new Command('foo'); $request = new Request('GET', 'http://www.example.com'); $mock = new MockHandler( [ function ($command, $request) { $this->assertArrayNotHasKey('delay', $command['@http']); return new AwsException('foo', $command, [ 'connection_error' => true ]); }, function ($command, $request) { $this->assertLessThanOrEqual(2000, $command['@http']['delay']); return new Result(); }, ], function () use (&$called) { $called[] = func_get_args(); }, function () use (&$called) { $called[] = func_get_args(); } ); $wrapped = new RetryMiddlewareV2( new Configuration('standard', 5), $mock, ['decider' => RetryMiddlewareV2::createDefaultDecider(new QuotaManager())] ); $result = $wrapped($command, $request)->wait(); $this->assertInstanceOf(ResultInterface::class, $result); $this->assertCount(2, $called); $this->assertInstanceOf(AwsException::class, $called[0][0]); $this->assertInstanceOf(ResultInterface::class, $called[1][0]); } public function testForwardRejectionWhenExceptionDoesNotMatch() { $command = new Command('foo'); $request = new Request('GET', 'http://www.example.com'); $mock = new MockHandler( [ function ($command, $request) { $this->assertArrayNotHasKey('delay', $command['@http']); return new AwsException('foo', $command); } ], function () use (&$called) { $called[] = func_get_args(); }, function () use (&$called) { $called[] = func_get_args(); } ); $wrapped = new RetryMiddlewareV2( new Configuration('standard', 5), $mock, ['decider' => RetryMiddlewareV2::createDefaultDecider(new QuotaManager())] ); try { $wrapped($command, $request)->wait(); $this->fail(); } catch (AwsException $e) { $this->assertCount(1, $called); $this->assertStringContainsString('foo', $e->getMessage()); } } public function testForwardValueWhenResultDoesNotMatch() { $command = new Command('foo'); $request = new Request('GET', 'http://www.example.com'); $res1 = new Result(); $mock = new MockHandler( [$res1], function () use (&$called) { $called[] = func_get_args(); }, function () use (&$called) { $called[] = func_get_args(); } ); $wrapped = new RetryMiddlewareV2( new Configuration('standard', 5), $mock, ['decider' => RetryMiddlewareV2::createDefaultDecider(new QuotaManager())] ); $result = $wrapped($command, $request)->wait(); $this->assertSame($res1, $result); $this->assertCount(1, $called); } public function testRetriesCanBeDisabledOnACommand() { $decider = RetryMiddlewareV2::createDefaultDecider( new QuotaManager(), 3 ); $command = new Command('foo', ['@retries' => 0]); $err = new AwsException('e', $command, ['connection_error' => true]); $this->assertFalse($decider(1, $command, $err)); } public function testResultReportsTheNumberOfRetries() { $handler = new MockHandler([ new Result(['@metadata' => ['statusCode' => '503']]), new Result(['@metadata' => ['statusCode' => '503']]), new Result(['@metadata' => ['statusCode' => '200']]), ]); $config = new Configuration('standard', 3); $retryMW = new RetryMiddlewareV2( $config, $handler, ['collect_stats' => true] ); $result = $retryMW(new Command('SomeCommand'), new Request('GET', '')) ->wait(); $this->assertArrayHasKey('retries_attempted', $result['@metadata']['transferStats']); $this->assertSame(2, $result['@metadata']['transferStats']['retries_attempted']); } public function testExceptionReportsTheNumberOfRetries() { $nextHandler = function (CommandInterface $command) { return new RejectedPromise( new AwsException('e', $command, ['connection_error' => true]) ); }; $config = new Configuration('standard', 3); $retryMW = new RetryMiddlewareV2( $config, $nextHandler, ['collect_stats' => true] ); try { $retryMW(new Command('SomeCommand'), new Request('GET', ''))->wait(); $this->fail(); } catch (AwsException $e) { $this->assertSame(2, $e->getTransferInfo('retries_attempted')); } } public function testResultReportsTotalRetryDelay() { $handler = new MockHandler([ new Result(['@metadata' => ['statusCode' => '503']]), new Result(['@metadata' => ['statusCode' => '503']]), new Result(['@metadata' => ['statusCode' => '503']]), new Result(['@metadata' => ['statusCode' => '200']]), ]); $config = new Configuration('standard', 4); $retryMW = new RetryMiddlewareV2( $config, $handler, ['collect_stats' => true] ); $result = $retryMW(new Command('SomeCommand'), new Request('GET', '')) ->wait(); $this->assertArrayHasKey('total_retry_delay', $result['@metadata']['transferStats']); // With 3 retries, expect total delay <= 2000 + 4000 + 8000 $this->assertLessThanOrEqual(14000, $result['@metadata']['transferStats']['total_retry_delay']); } public function testExceptionReportsTotalRetryDelay() { $nextHandler = function (CommandInterface $command) { return new RejectedPromise( new AwsException('e', $command, ['connection_error' => true]) ); }; $config = new Configuration('standard', 4); $retryMW = new RetryMiddlewareV2( $config, $nextHandler, ['collect_stats' => true] ); try { $retryMW(new Command('SomeCommand'), new Request('GET', ''))->wait(); $this->fail(); } catch (AwsException $e) { // With 3 retries, expect total delay <= 2000 + 4000 + 8000 $this->assertLessThanOrEqual(14000, $e->getTransferInfo('total_retry_delay')); } } public function testReportsHttpStatsForEachRequest() { $handler = new MockHandler([ new Result(['@metadata' => [ 'statusCode' => '503', 'transferStats' => ['http' => [['foo' => 'bar']]] ]]), new Result(['@metadata' => [ 'statusCode' => '503', 'transferStats' => ['http' => [['baz' => 'quux']]] ]]), new Result(['@metadata' => [ 'statusCode' => '200', 'transferStats' => ['http' => [['fizz' => 'buzz']]] ]]), ]); $config = new Configuration('standard', 4); $retryMW = new RetryMiddlewareV2( $config, $handler, ['collect_stats' => true] ); $result = $retryMW(new Command('SomeCommand'), new Request('GET', '')) ->wait(); $httpStats = $result['@metadata']['transferStats']['http']; $this->assertCount(3, $httpStats); $this->assertSame([ ['foo' => 'bar'], ['baz' => 'quux'], ['fizz' => 'buzz'], ], $httpStats); } public function testReportsHttpStatsForEachException() { $command = new Command('TestCommand'); $response = new Response(500); $handler = new MockHandler([ new AwsException( 'Test Exception', $command, [ 'response' => $response, 'transfer_stats' => [ 'starttransfer_time' => 5, 'appconnect_time' => 4 ] ] ), new AwsException( 'Test Exception', $command, [ 'response' => $response, 'transfer_stats' => [ 'starttransfer_time' => 10, 'appconnect_time' => 8 ] ] ), new AwsException( 'Test Exception', $command, [ 'response' => $response, 'transfer_stats' => [ 'starttransfer_time' => 15, 'appconnect_time' => 12 ] ] ), new AwsException( 'Test Exception', $command, [ 'response' => $response, 'transfer_stats' => [ 'starttransfer_time' => 20, 'appconnect_time' => 16 ] ] ) ]); $config = new Configuration('standard', 4); $retryMW = new RetryMiddlewareV2( $config, $handler, ['collect_stats' => true] ); try { $retryMW(new Command('SomeCommand'), new Request('GET', '')) ->wait(); $this->fail('This command should have produced an AwsException.'); } catch (AwsException $e) { $stats= $e->getTransferInfo(); $this->assertEquals( [ [ 'starttransfer_time' => 5, 'appconnect_time' => 4 ], [ 'starttransfer_time' => 10, 'appconnect_time' => 8 ], [ 'starttransfer_time' => 15, 'appconnect_time' => 12 ], [ 'starttransfer_time' => 20, 'appconnect_time' => 16 ], ], $stats['http'] ); } } public function testReportsHttpStatsForEachRequestEvenIfRetryStatsDisabled() { $handler = new MockHandler([ new Result(['@metadata' => [ 'statusCode' => '503', 'transferStats' => ['http' => [['foo' => 'bar']]] ]]), new Result(['@metadata' => [ 'statusCode' => '503', 'transferStats' => ['http' => [['baz' => 'quux']]] ]]), new Result(['@metadata' => [ 'statusCode' => '200', 'transferStats' => ['http' => [['fizz' => 'buzz']]] ]]), ]); $config = new Configuration('standard', 4); $retryMW = new RetryMiddlewareV2( $config, $handler, ['collect_stats' => true] ); $result = $retryMW(new Command('SomeCommand'), new Request('GET', '')) ->wait(); $httpStats = $result['@metadata']['transferStats']['http']; $this->assertCount(3, $httpStats); $this->assertSame([ ['foo' => 'bar'], ['baz' => 'quux'], ['fizz' => 'buzz'], ], $httpStats); } }