113 lines
		
	
	
		
			3.2 KiB
		
	
	
	
		
			PHP
		
	
	
	
	
	
			
		
		
	
	
			113 lines
		
	
	
		
			3.2 KiB
		
	
	
	
		
			PHP
		
	
	
	
	
	
| <?php
 | |
| namespace GuzzleHttp;
 | |
| 
 | |
| use GuzzleHttp\Promise\PromiseInterface;
 | |
| use GuzzleHttp\Promise\RejectedPromise;
 | |
| use GuzzleHttp\Psr7;
 | |
| use Psr\Http\Message\RequestInterface;
 | |
| use Psr\Http\Message\ResponseInterface;
 | |
| 
 | |
| /**
 | |
|  * Middleware that retries requests based on the boolean result of
 | |
|  * invoking the provided "decider" function.
 | |
|  */
 | |
| class RetryMiddleware
 | |
| {
 | |
|     /** @var callable  */
 | |
|     private $nextHandler;
 | |
| 
 | |
|     /** @var callable */
 | |
|     private $decider;
 | |
| 
 | |
|     /**
 | |
|      * @param callable $decider     Function that accepts the number of retries,
 | |
|      *                              a request, [response], and [exception] and
 | |
|      *                              returns true if the request is to be
 | |
|      *                              retried.
 | |
|      * @param callable $nextHandler Next handler to invoke.
 | |
|      * @param callable $delay       Function that accepts the number of retries
 | |
|      *                              and [response] and returns the number of
 | |
|      *                              milliseconds to delay.
 | |
|      */
 | |
|     public function __construct(
 | |
|         callable $decider,
 | |
|         callable $nextHandler,
 | |
|         callable $delay = null
 | |
|     ) {
 | |
|         $this->decider = $decider;
 | |
|         $this->nextHandler = $nextHandler;
 | |
|         $this->delay = $delay ?: __CLASS__ . '::exponentialDelay';
 | |
|     }
 | |
| 
 | |
|     /**
 | |
|      * Default exponential backoff delay function.
 | |
|      *
 | |
|      * @param $retries
 | |
|      *
 | |
|      * @return int
 | |
|      */
 | |
|     public static function exponentialDelay($retries)
 | |
|     {
 | |
|         return (int) pow(2, $retries - 1);
 | |
|     }
 | |
| 
 | |
|     /**
 | |
|      * @param RequestInterface $request
 | |
|      * @param array            $options
 | |
|      *
 | |
|      * @return PromiseInterface
 | |
|      */
 | |
|     public function __invoke(RequestInterface $request, array $options)
 | |
|     {
 | |
|         if (!isset($options['retries'])) {
 | |
|             $options['retries'] = 0;
 | |
|         }
 | |
| 
 | |
|         $fn = $this->nextHandler;
 | |
|         return $fn($request, $options)
 | |
|             ->then(
 | |
|                 $this->onFulfilled($request, $options),
 | |
|                 $this->onRejected($request, $options)
 | |
|             );
 | |
|     }
 | |
| 
 | |
|     private function onFulfilled(RequestInterface $req, array $options)
 | |
|     {
 | |
|         return function ($value) use ($req, $options) {
 | |
|             if (!call_user_func(
 | |
|                 $this->decider,
 | |
|                 $options['retries'],
 | |
|                 $req,
 | |
|                 $value,
 | |
|                 null
 | |
|             )) {
 | |
|                 return $value;
 | |
|             }
 | |
|             return $this->doRetry($req, $options, $value);
 | |
|         };
 | |
|     }
 | |
| 
 | |
|     private function onRejected(RequestInterface $req, array $options)
 | |
|     {
 | |
|         return function ($reason) use ($req, $options) {
 | |
|             if (!call_user_func(
 | |
|                 $this->decider,
 | |
|                 $options['retries'],
 | |
|                 $req,
 | |
|                 null,
 | |
|                 $reason
 | |
|             )) {
 | |
|                 return new RejectedPromise($reason);
 | |
|             }
 | |
|             return $this->doRetry($req, $options);
 | |
|         };
 | |
|     }
 | |
| 
 | |
|     private function doRetry(RequestInterface $request, array $options, ResponseInterface $response = null)
 | |
|     {
 | |
|         $options['delay'] = call_user_func($this->delay, ++$options['retries'], $response);
 | |
| 
 | |
|         return $this($request, $options);
 | |
|     }
 | |
| }
 |