| Viewing file:  BackoffPlugin.php (4.34 KB)      -rw-r--r-- Select action/file-type:
 
  (+) |  (+) |  (+) | Code (+) | Session (+) |  (+) | SDB (+) |  (+) |  (+) |  (+) |  (+) |  (+) | 
 
<?php
 namespace Guzzle\Plugin\Backoff;
 
 use Guzzle\Common\Event;
 use Guzzle\Common\AbstractHasDispatcher;
 use Guzzle\Http\Message\EntityEnclosingRequestInterface;
 use Guzzle\Http\Message\RequestInterface;
 use Guzzle\Http\Message\Response;
 use Guzzle\Http\Curl\CurlMultiInterface;
 use Guzzle\Http\Exception\CurlException;
 use Symfony\Component\EventDispatcher\EventSubscriberInterface;
 
 /**
 * Plugin to automatically retry failed HTTP requests using a backoff strategy
 */
 class BackoffPlugin extends AbstractHasDispatcher implements EventSubscriberInterface
 {
 const DELAY_PARAM = CurlMultiInterface::BLOCKING;
 const RETRY_PARAM = 'plugins.backoff.retry_count';
 const RETRY_EVENT = 'plugins.backoff.retry';
 
 /** @var BackoffStrategyInterface Backoff strategy */
 protected $strategy;
 
 /**
 * @param BackoffStrategyInterface $strategy The backoff strategy used to determine whether or not to retry and
 *                                           the amount of delay between retries.
 */
 public function __construct(BackoffStrategyInterface $strategy = null)
 {
 $this->strategy = $strategy;
 }
 
 /**
 * Retrieve a basic truncated exponential backoff plugin that will retry HTTP errors and cURL errors
 *
 * @param int   $maxRetries Maximum number of retries
 * @param array $httpCodes  HTTP response codes to retry
 * @param array $curlCodes  cURL error codes to retry
 *
 * @return self
 */
 public static function getExponentialBackoff(
 $maxRetries = 3,
 array $httpCodes = null,
 array $curlCodes = null
 ) {
 return new self(new TruncatedBackoffStrategy($maxRetries,
 new HttpBackoffStrategy($httpCodes,
 new CurlBackoffStrategy($curlCodes,
 new ExponentialBackoffStrategy()
 )
 )
 ));
 }
 
 public static function getAllEvents()
 {
 return array(self::RETRY_EVENT);
 }
 
 public static function getSubscribedEvents()
 {
 return array(
 'request.sent'      => 'onRequestSent',
 'request.exception' => 'onRequestSent',
 CurlMultiInterface::POLLING_REQUEST => 'onRequestPoll'
 );
 }
 
 /**
 * Called when a request has been sent  and isn't finished processing
 *
 * @param Event $event
 */
 public function onRequestSent(Event $event)
 {
 $request = $event['request'];
 $response = $event['response'];
 $exception = $event['exception'];
 
 $params = $request->getParams();
 $retries = (int) $params->get(self::RETRY_PARAM);
 $delay = $this->strategy->getBackoffPeriod($retries, $request, $response, $exception);
 
 if ($delay !== false) {
 // Calculate how long to wait until the request should be retried
 $params->set(self::RETRY_PARAM, ++$retries)
 ->set(self::DELAY_PARAM, microtime(true) + $delay);
 // Send the request again
 $request->setState(RequestInterface::STATE_TRANSFER);
 $this->dispatch(self::RETRY_EVENT, array(
 'request'  => $request,
 'response' => $response,
 'handle'   => ($exception && $exception instanceof CurlException) ? $exception->getCurlHandle() : null,
 'retries'  => $retries,
 'delay'    => $delay
 ));
 }
 }
 
 /**
 * Called when a request is polling in the curl multi object
 *
 * @param Event $event
 */
 public function onRequestPoll(Event $event)
 {
 $request = $event['request'];
 $delay = $request->getParams()->get(self::DELAY_PARAM);
 
 // If the duration of the delay has passed, retry the request using the pool
 if (null !== $delay && microtime(true) >= $delay) {
 // Remove the request from the pool and then add it back again. This is required for cURL to know that we
 // want to retry sending the easy handle.
 $request->getParams()->remove(self::DELAY_PARAM);
 // Rewind the request body if possible
 if ($request instanceof EntityEnclosingRequestInterface && $request->getBody()) {
 $request->getBody()->seek(0);
 }
 $multi = $event['curl_multi'];
 $multi->remove($request);
 $multi->add($request);
 }
 }
 }
 
 |