| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277 | 
							- <?php
 
- namespace Aws;
 
- use Aws\Exception\AwsException;
 
- use Aws\Retry\RetryHelperTrait;
 
- use GuzzleHttp\Exception\RequestException;
 
- use Psr\Http\Message\RequestInterface;
 
- use GuzzleHttp\Promise\PromiseInterface;
 
- use GuzzleHttp\Promise;
 
- /**
 
-  * Middleware that retries failures. V1 implemention that supports 'legacy' mode.
 
-  *
 
-  * @internal
 
-  */
 
- class RetryMiddleware
 
- {
 
-     use RetryHelperTrait;
 
-     private static $retryStatusCodes = [
 
-         500 => true,
 
-         502 => true,
 
-         503 => true,
 
-         504 => true
 
-     ];
 
-     private static $retryCodes = [
 
-         // Throttling error
 
-         'RequestLimitExceeded'                   => true,
 
-         'Throttling'                             => true,
 
-         'ThrottlingException'                    => true,
 
-         'ThrottledException'                     => true,
 
-         'ProvisionedThroughputExceededException' => true,
 
-         'RequestThrottled'                       => true,
 
-         'BandwidthLimitExceeded'                 => true,
 
-         'RequestThrottledException'              => true,
 
-         'TooManyRequestsException'               => true,
 
-         'IDPCommunicationError'                  => true,
 
-         'EC2ThrottledException'                  => true,
 
-     ];
 
-     private $decider;
 
-     private $delay;
 
-     private $nextHandler;
 
-     private $collectStats;
 
-     public function __construct(
 
-         callable $decider,
 
-         callable $delay,
 
-         callable $nextHandler,
 
-         $collectStats = false
 
-     ) {
 
-         $this->decider = $decider;
 
-         $this->delay = $delay;
 
-         $this->nextHandler = $nextHandler;
 
-         $this->collectStats = (bool) $collectStats;
 
-     }
 
-     /**
 
-      * Creates a default AWS retry decider function.
 
-      *
 
-      * The optional $extraConfig parameter is an associative array
 
-      * that specifies additional retry conditions on top of the ones specified
 
-      * by default by the Aws\RetryMiddleware class, with the following keys:
 
-      *
 
-      * - errorCodes: (string[]) An indexed array of AWS exception codes to retry.
 
-      *   Optional.
 
-      * - statusCodes: (int[]) An indexed array of HTTP status codes to retry.
 
-      *   Optional.
 
-      * - curlErrors: (int[]) An indexed array of Curl error codes to retry. Note
 
-      *   these should be valid Curl constants. Optional.
 
-      *
 
-      * @param int $maxRetries
 
-      * @param array $extraConfig
 
-      * @return callable
 
-      */
 
-     public static function createDefaultDecider(
 
-         $maxRetries = 3,
 
-         $extraConfig = []
 
-     ) {
 
-         $retryCurlErrors = [];
 
-         if (extension_loaded('curl')) {
 
-             $retryCurlErrors[CURLE_RECV_ERROR] = true;
 
-         }
 
-         return function (
 
-             $retries,
 
-             CommandInterface $command,
 
-             RequestInterface $request,
 
-             ResultInterface $result = null,
 
-             $error = null
 
-         ) use ($maxRetries, $retryCurlErrors, $extraConfig) {
 
-             // Allow command-level options to override this value
 
-             $maxRetries = null !== $command['@retries'] ?
 
-                 $command['@retries']
 
-                 : $maxRetries;
 
-             $isRetryable = self::isRetryable(
 
-                 $result,
 
-                 $error,
 
-                 $retryCurlErrors,
 
-                 $extraConfig
 
-             );
 
-             if ($retries >= $maxRetries) {
 
-                 if (!empty($error)
 
-                     && $error instanceof AwsException
 
-                     && $isRetryable
 
-                 ) {
 
-                     $error->setMaxRetriesExceeded();
 
-                 }
 
-                 return false;
 
-             }
 
-             return $isRetryable;
 
-         };
 
-     }
 
-     private static function isRetryable(
 
-         $result,
 
-         $error,
 
-         $retryCurlErrors,
 
-         $extraConfig = []
 
-     ) {
 
-         $errorCodes = self::$retryCodes;
 
-         if (!empty($extraConfig['error_codes'])
 
-             && is_array($extraConfig['error_codes'])
 
-         ) {
 
-             foreach($extraConfig['error_codes'] as $code) {
 
-                 $errorCodes[$code] = true;
 
-             }
 
-         }
 
-         $statusCodes = self::$retryStatusCodes;
 
-         if (!empty($extraConfig['status_codes'])
 
-             && is_array($extraConfig['status_codes'])
 
-         ) {
 
-             foreach($extraConfig['status_codes'] as $code) {
 
-                 $statusCodes[$code] = true;
 
-             }
 
-         }
 
-         if (!empty($extraConfig['curl_errors'])
 
-             && is_array($extraConfig['curl_errors'])
 
-         ) {
 
-             foreach($extraConfig['curl_errors'] as $code) {
 
-                 $retryCurlErrors[$code] = true;
 
-             }
 
-         }
 
-         if (!$error) {
 
-             if (!isset($result['@metadata']['statusCode'])) {
 
-                 return false;
 
-             }
 
-             return isset($statusCodes[$result['@metadata']['statusCode']]);
 
-         }
 
-         if (!($error instanceof AwsException)) {
 
-             return false;
 
-         }
 
-         if ($error->isConnectionError()) {
 
-             return true;
 
-         }
 
-         if (isset($errorCodes[$error->getAwsErrorCode()])) {
 
-             return true;
 
-         }
 
-         if (isset($statusCodes[$error->getStatusCode()])) {
 
-             return true;
 
-         }
 
-         if (count($retryCurlErrors)
 
-             && ($previous = $error->getPrevious())
 
-             && $previous instanceof RequestException
 
-         ) {
 
-             if (method_exists($previous, 'getHandlerContext')) {
 
-                 $context = $previous->getHandlerContext();
 
-                 return !empty($context['errno'])
 
-                     && isset($retryCurlErrors[$context['errno']]);
 
-             }
 
-             $message = $previous->getMessage();
 
-             foreach (array_keys($retryCurlErrors) as $curlError) {
 
-                 if (strpos($message, 'cURL error ' . $curlError . ':') === 0) {
 
-                     return true;
 
-                 }
 
-             }
 
-         }
 
-         return false;
 
-     }
 
-     /**
 
-      * Delay function that calculates an exponential delay.
 
-      *
 
-      * Exponential backoff with jitter, 100ms base, 20 sec ceiling
 
-      *
 
-      * @param $retries - The number of retries that have already been attempted
 
-      *
 
-      * @return int
 
-      *
 
-      * @link https://aws.amazon.com/blogs/architecture/exponential-backoff-and-jitter/
 
-      */
 
-     public static function exponentialDelay($retries)
 
-     {
 
-         return mt_rand(0, (int) min(20000, (int) pow(2, $retries) * 100));
 
-     }
 
-     /**
 
-      * @param CommandInterface $command
 
-      * @param RequestInterface $request
 
-      *
 
-      * @return PromiseInterface
 
-      */
 
-     public function __invoke(
 
-         CommandInterface $command,
 
-         RequestInterface $request = null
 
-     ) {
 
-         $retries = 0;
 
-         $requestStats = [];
 
-         $monitoringEvents = [];
 
-         $handler = $this->nextHandler;
 
-         $decider = $this->decider;
 
-         $delay = $this->delay;
 
-         $request = $this->addRetryHeader($request, 0, 0);
 
-         $g = function ($value) use (
 
-             $handler,
 
-             $decider,
 
-             $delay,
 
-             $command,
 
-             $request,
 
-             &$retries,
 
-             &$requestStats,
 
-             &$monitoringEvents,
 
-             &$g
 
-         ) {
 
-             $this->updateHttpStats($value, $requestStats);
 
-             if ($value instanceof MonitoringEventsInterface) {
 
-                 $reversedEvents = array_reverse($monitoringEvents);
 
-                 $monitoringEvents = array_merge($monitoringEvents, $value->getMonitoringEvents());
 
-                 foreach ($reversedEvents as $event) {
 
-                     $value->prependMonitoringEvent($event);
 
-                 }
 
-             }
 
-             if ($value instanceof \Exception || $value instanceof \Throwable) {
 
-                 if (!$decider($retries, $command, $request, null, $value)) {
 
-                     return Promise\Create::rejectionFor(
 
-                         $this->bindStatsToReturn($value, $requestStats)
 
-                     );
 
-                 }
 
-             } elseif ($value instanceof ResultInterface
 
-                 && !$decider($retries, $command, $request, $value, null)
 
-             ) {
 
-                 return $this->bindStatsToReturn($value, $requestStats);
 
-             }
 
-             // Delay fn is called with 0, 1, ... so increment after the call.
 
-             $delayBy = $delay($retries++);
 
-             $command['@http']['delay'] = $delayBy;
 
-             if ($this->collectStats) {
 
-                 $this->updateStats($retries, $delayBy, $requestStats);
 
-             }
 
-             // Update retry header with retry count and delayBy
 
-             $request = $this->addRetryHeader($request, $retries, $delayBy);
 
-             return $handler($command, $request)->then($g, $g);
 
-         };
 
-         return $handler($command, $request)->then($g, $g);
 
-     }
 
- }
 
 
  |