1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13: 14: 15: 16: 17: 18: 19: 20: 21: 22: 23: 24: 25: 26: 27: 28: 29: 30: 31: 32: 33: 34: 35: 36: 37: 38: 39: 40: 41: 42: 43: 44: 45: 46: 47: 48: 49: 50: 51: 52: 53: 54: 55: 56: 57: 58: 59: 60: 61: 62: 63: 64: 65: 66: 67: 68: 69: 70: 71: 72: 73: 74: 75: 76: 77: 78: 79: 80: 81: 82: 83: 84: 85: 86: 87: 88: 89: 90: 91: 92: 93: 94: 95: 96: 97: 98: 99: 100: 101: 102: 103: 104: 105: 106: 107: 108: 109: 110: 111: 112: 113: 114: 115: 116: 117: 118: 119: 120: 121: 122: 123: 124: 125: 126: 127: 128: 129: 130: 131: 132: 133: 134: 135: 136: 137: 138: 139: 140: 141: 142: 143: 144: 145: 146: 147: 148: 149: 150: 151: 152: 153: 154: 155: 156: 157: 158: 159: 160: 161: 162: 163: 164: 165: 166: 167: 168: 169: 170: 171: 172: 173: 174: 175: 176: 177: 178: 179: 180: 181: 182: 183: 184: 185: 186: 187: 188: 189: 190: 191: 192: 193: 194: 195: 196: 197: 198: 199: 200: 201: 202: 203: 204: 205: 206: 207: 208: 209: 210: 211: 212: 213: 214: 215: 216: 217: 218: 219: 220: 221: 222: 223: 224: 225: 226: 227: 228: 229: 230: 231: 232: 233: 234: 235: 236: 237: 238: 239: 240: 241: 242: 243: 244: 245: 246: 247: 248: 249: 250: 251: 252: 253: 254: 255: 256: 257: 258: 259: 260: 261: 262: 263: 264: 265: 266: 267: 268: 269: 270: 271: 272: 273: 274: 275: 276: 277: 278: 279: 280: 281: 282: 283: 284: 285: 286: 287: 288: 289: 290: 291: 292: 293: 294: 295: 296: 297: 298:
<?php
namespace Guzzle\Tests\Plugin\Backoff;
use Guzzle\Common\Event;
use Guzzle\Http\Exception\CurlException;
use Guzzle\Http\Client;
use Guzzle\Plugin\Backoff\BackoffPlugin;
use Guzzle\Http\Message\RequestInterface;
use Guzzle\Http\Message\Request;
use Guzzle\Http\Message\EntityEnclosingRequest;
use Guzzle\Http\Message\Response;
use Guzzle\Http\Curl\CurlMulti;
use Guzzle\Http\Curl\CurlMultiInterface;
use Guzzle\Plugin\Backoff\ConstantBackoffStrategy;
use Guzzle\Plugin\Backoff\CurlBackoffStrategy;
use Guzzle\Plugin\Backoff\HttpBackoffStrategy;
use Guzzle\Plugin\Backoff\TruncatedBackoffStrategy;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
class BackoffPluginTest extends \Guzzle\Tests\GuzzleTestCase implements EventSubscriberInterface
{
protected $retried;
public function setUp()
{
$this->retried = false;
}
public static function getSubscribedEvents()
{
return array(BackoffPlugin::RETRY_EVENT => 'onRequestRetry');
}
public function onRequestRetry(Event $event)
{
$this->retried = $event;
}
public function testHasEventList()
{
$this->assertEquals(1, count(BackoffPlugin::getAllEvents()));
}
public function testCreatesDefaultExponentialBackoffPlugin()
{
$plugin = BackoffPlugin::getExponentialBackoff(3, array(204), array(10));
$this->assertInstanceOf('Guzzle\Plugin\Backoff\BackoffPlugin', $plugin);
$strategy = $this->readAttribute($plugin, 'strategy');
$this->assertInstanceOf('Guzzle\Plugin\Backoff\TruncatedBackoffStrategy', $strategy);
$this->assertEquals(3, $this->readAttribute($strategy, 'max'));
$strategy = $this->readAttribute($strategy, 'next');
$this->assertInstanceOf('Guzzle\Plugin\Backoff\HttpBackoffStrategy', $strategy);
$this->assertEquals(array(204 => true), $this->readAttribute($strategy, 'errorCodes'));
$strategy = $this->readAttribute($strategy, 'next');
$this->assertInstanceOf('Guzzle\Plugin\Backoff\CurlBackoffStrategy', $strategy);
$this->assertEquals(array(10 => true), $this->readAttribute($strategy, 'errorCodes'));
$strategy = $this->readAttribute($strategy, 'next');
$this->assertInstanceOf('Guzzle\Plugin\Backoff\ExponentialBackoffStrategy', $strategy);
}
public function testDoesNotRetryUnlessStrategyReturnsNumber()
{
$request = new Request('GET', 'http://www.example.com');
$request->setState('transfer');
$mock = $this->getMockBuilder('Guzzle\Plugin\Backoff\BackoffStrategyInterface')
->setMethods(array('getBackoffPeriod'))
->getMockForAbstractClass();
$mock->expects($this->once())
->method('getBackoffPeriod')
->will($this->returnValue(false));
$plugin = new BackoffPlugin($mock);
$plugin->addSubscriber($this);
$plugin->onRequestSent(new Event(array('request' => $request)));
$this->assertFalse($this->retried);
}
public function testUpdatesRequestForRetry()
{
$request = new Request('GET', 'http://www.example.com');
$request->setState('transfer');
$response = new Response(500);
$handle = $this->getMockBuilder('Guzzle\Http\Curl\CurlHandle')->disableOriginalConstructor()->getMock();
$e = new CurlException();
$e->setCurlHandle($handle);
$plugin = new BackoffPlugin(new ConstantBackoffStrategy(10));
$plugin->addSubscriber($this);
$event = new Event(array(
'request' => $request,
'response' => $response,
'exception' => $e
));
$plugin->onRequestSent($event);
$this->assertEquals(array(
'request' => $request,
'response' => $response,
'handle' => $handle,
'retries' => 1,
'delay' => 10
), $this->readAttribute($this->retried, 'context'));
$plugin->onRequestSent($event);
$this->assertEquals(array(
'request' => $request,
'response' => $response,
'handle' => $handle,
'retries' => 2,
'delay' => 10
), $this->readAttribute($this->retried, 'context'));
}
public function testDoesNothingWhenNotRetryingAndPollingRequest()
{
$request = new Request('GET', 'http://www.foo.com');
$plugin = new BackoffPlugin(new ConstantBackoffStrategy(10));
$plugin->onRequestPoll(new Event(array('request' => $request)));
}
public function testRetriesRequests()
{
$this->getServer()->flush();
$this->getServer()->enqueue(array(
"HTTP/1.1 500 Internal Server Error\r\nContent-Length: 0\r\n\r\n",
"HTTP/1.1 500 Internal Server Error\r\nContent-Length: 0\r\n\r\n",
"HTTP/1.1 200 OK\r\nContent-Length: 4\r\n\r\ndata"
));
$plugin = new BackoffPlugin(
new TruncatedBackoffStrategy(3,
new HttpBackoffStrategy(null,
new CurlBackoffStrategy(null,
new ConstantBackoffStrategy(0.05)
)
)
)
);
$client = new Client($this->getServer()->getUrl());
$client->getEventDispatcher()->addSubscriber($plugin);
$request = $client->get();
$request->send();
$this->assertEquals(200, $request->getResponse()->getStatusCode());
$this->assertEquals('data', $request->getResponse()->getBody(true));
$this->assertEquals(3, count($this->getServer()->getReceivedRequests(false)));
$this->assertEquals(2, $request->getParams()->get(BackoffPlugin::RETRY_PARAM));
}
public function testFailsOnTruncation()
{
$this->getServer()->flush();
$this->getServer()->enqueue(array(
"HTTP/1.1 500 Internal Server Error\r\nContent-Length: 0\r\n\r\n",
"HTTP/1.1 500 Internal Server Error\r\nContent-Length: 0\r\n\r\n"
));
$plugin = new BackoffPlugin(
new TruncatedBackoffStrategy(2,
new HttpBackoffStrategy(null,
new ConstantBackoffStrategy(0.05)
)
)
);
$client = new Client($this->getServer()->getUrl());
$client->addSubscriber($plugin);
$client->get()->send();
}
public function testRetriesRequestsWhenInParallel()
{
$this->getServer()->flush();
$this->getServer()->enqueue(array(
"HTTP/1.1 500 Internal Server Error\r\nContent-Length: 0\r\n\r\n",
"HTTP/1.1 500 Internal Server Error\r\nContent-Length: 0\r\n\r\n",
"HTTP/1.1 500 Internal Server Error\r\nContent-Length: 0\r\n\r\n",
"HTTP/1.1 500 Internal Server Error\r\nContent-Length: 0\r\n\r\n",
"HTTP/1.1 500 Internal Server Error\r\nContent-Length: 0\r\n\r\n",
"HTTP/1.1 500 Internal Server Error\r\nContent-Length: 0\r\n\r\n",
"HTTP/1.1 500 Internal Server Error\r\nContent-Length: 0\r\n\r\n",
"HTTP/1.1 500 Internal Server Error\r\nContent-Length: 0\r\n\r\n",
"HTTP/1.1 500 Internal Server Error\r\nContent-Length: 0\r\n\r\n",
"HTTP/1.1 500 Internal Server Error\r\nContent-Length: 0\r\n\r\n",
"HTTP/1.1 200 OK\r\nContent-Length: 4\r\n\r\ndata",
"HTTP/1.1 200 OK\r\nContent-Length: 4\r\n\r\ndata",
"HTTP/1.1 200 OK\r\nContent-Length: 4\r\n\r\ndata",
"HTTP/1.1 200 OK\r\nContent-Length: 4\r\n\r\ndata",
"HTTP/1.1 200 OK\r\nContent-Length: 4\r\n\r\ndata"
));
$plugin = new BackoffPlugin(
new HttpBackoffStrategy(null,
new TruncatedBackoffStrategy(3,
new CurlBackoffStrategy(null,
new ConstantBackoffStrategy(0.1)
)
)
)
);
$client = new Client($this->getServer()->getUrl());
$client->getEventDispatcher()->addSubscriber($plugin);
$requests = array();
for ($i = 0; $i < 5; $i++) {
$requests[] = $client->get();
}
$client->send($requests);
$this->assertEquals(15, count($this->getServer()->getReceivedRequests(false)));
}
public function testRetriesPooledRequestsUsingDelayAndPollingEvent()
{
$this->getServer()->flush();
$this->getServer()->enqueue(array(
"HTTP/1.1 500 Internal Server Error\r\nContent-Length: 0\r\n\r\n",
"HTTP/1.1 200 OK\r\nContent-Length: 4\r\n\r\ndata"
));
$plugin = new BackoffPlugin(new HttpBackoffStrategy(null,
new TruncatedBackoffStrategy(1,
new ConstantBackoffStrategy(0.5))));
$client = new Client($this->getServer()->getUrl());
$client->getEventDispatcher()->addSubscriber($plugin);
$request = $client->get();
$request->send();
$this->assertEquals('data', $request->getResponse()->getBody(true));
$this->assertEquals(2, count($this->getServer()->getReceivedRequests(false)));
}
public function testSeeksToBeginningOfRequestBodyWhenRetrying()
{
$request = new EntityEnclosingRequest('PUT', 'http://www.example.com');
$request->setBody('abc');
$request->getParams()->set(BackoffPlugin::DELAY_PARAM, 2);
$request->getBody()->seek(3);
$this->assertEquals('', $request->getBody()->read(1));
$plugin = new BackoffPlugin(new ConstantBackoffStrategy(0));
$plugin->onRequestPoll($this->getMockEvent($request));
$this->assertEquals('a', $request->getBody()->read(1));
}
public function testDoesNotSeekOnRequestsWithNoBodyWhenRetrying()
{
$request = new EntityEnclosingRequest('PUT', 'http://www.example.com');
$request->getParams()->set(BackoffPlugin::DELAY_PARAM, 2);
$plugin = new BackoffPlugin(new ConstantBackoffStrategy(0));
$plugin->onRequestPoll($this->getMockEvent($request));
}
protected function getMockEvent(RequestInterface $request)
{
$multi = $this->getMockBuilder('Guzzle\Http\Curl\CurlMulti')
->setMethods(array('remove', 'add'))
->getMock();
$event = new Event(array(
'request' => $request,
'curl_multi' => $multi
));
$event->setName(CurlMultiInterface::POLLING_REQUEST);
return $event;
}
}