Skip to content

Commit c8a8a5f

Browse files
committed
Throw exceptions for error status codes
1 parent b2cefe9 commit c8a8a5f

File tree

6 files changed

+91
-48
lines changed

6 files changed

+91
-48
lines changed

src/Exception/ProxyResponseException.php

Lines changed: 7 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -11,27 +11,26 @@
1111

1212
namespace FOS\HttpCache\Exception;
1313

14-
use Http\Adapter\Exception\HttpAdapterException;
14+
use Psr\Http\Message\ResponseInterface;
1515

1616
/**
1717
* Wrapping an error response from the caching proxy.
1818
*/
1919
class ProxyResponseException extends \RuntimeException implements HttpCacheExceptionInterface
2020
{
2121
/**
22-
* @param HttpAdapterException $adapterException HTTP adapter exception.
22+
* @param ResponseInterface $response HTTP response
2323
*
2424
* @return ProxyResponseException
2525
*/
26-
public static function proxyResponse(HttpAdapterException $adapterException)
26+
public static function proxyResponse(ResponseInterface $response)
2727
{
2828
$message = sprintf(
29-
'%s error response "%s" from caching proxy at %s',
30-
$adapterException->getResponse()->getStatusCode(),
31-
$adapterException->getResponse()->getReasonPhrase(),
32-
$adapterException->getRequest()->getHeaderLine('Host')
29+
'%s error response "%s" from caching proxy',
30+
$response->getStatusCode(),
31+
$response->getReasonPhrase()
3332
);
3433

35-
return new ProxyResponseException($message, 0, $adapterException);
34+
return new ProxyResponseException($message, 0);
3635
}
3736
}

src/ProxyClient/AbstractProxyClient.php

Lines changed: 34 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
use Http\Adapter\Exception\MultiHttpAdapterException;
2020
use Http\Adapter\HttpAdapter;
2121
use Http\Discovery\HttpAdapterDiscovery;
22+
use Psr\Http\Message\ResponseInterface;
2223

2324
/**
2425
* Abstract caching proxy client
@@ -77,27 +78,33 @@ public function flush()
7778
$this->queue->clear();
7879

7980
try {
80-
$this->httpAdapter->sendRequests($queue->all());
81+
$responses = $this->httpAdapter->sendRequests($queue->all());
8182
} catch (MultiHttpAdapterException $e) {
83+
// Handle all networking errors: php-http only throws an exception
84+
// if no response is available.
8285
$collection = new ExceptionCollection();
8386
foreach ($e->getExceptions() as $exception) {
84-
// A workaround for php-http currently lacking differentiation
85-
// between client, server and networking errors.
87+
// php-http only throws an exception if no response is available
8688
if (!$exception->getResponse()) {
8789
// Assume networking error if no response was returned.
8890
$collection->add(
8991
ProxyUnreachableException::proxyUnreachable($exception)
9092
);
91-
} else {
92-
$collection->add(
93-
ProxyResponseException::proxyResponse($exception)
94-
);
9593
}
9694
}
9795

96+
foreach ($this->handleErrorResponses($e->getResponses()) as $exception) {
97+
$collection->add($exception);
98+
}
99+
98100
throw $collection;
99101
}
100102

103+
$exceptions = $this->handleErrorResponses($responses);
104+
if (count($exceptions) > 0) {
105+
throw new ExceptionCollection($exceptions);
106+
}
107+
101108
return count($queue);
102109
}
103110

@@ -123,4 +130,24 @@ protected function initQueue(array $servers, $baseUri)
123130
{
124131
$this->queue = new RequestQueue($servers, $baseUri);
125132
}
133+
134+
/**
135+
* @param ResponseInterface[] $responses
136+
*
137+
* @return ProxyResponseException[]
138+
*/
139+
private function handleErrorResponses(array $responses)
140+
{
141+
$exceptions = [];
142+
143+
foreach ($responses as $response) {
144+
if ($response->getStatusCode() >= 400
145+
&& $response->getStatusCode() < 600
146+
) {
147+
$exceptions[] = ProxyResponseException::proxyResponse($response);
148+
}
149+
}
150+
151+
return $exceptions;
152+
}
126153
}

src/Test/HttpClient/MockHttpAdapter.php

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,9 @@
55
use Http\Adapter\Exception\MultiHttpAdapterException;
66
use Http\Adapter\HttpAdapter;
77
use GuzzleHttp\Psr7\Response;
8+
use Http\Discovery\MessageFactoryDiscovery;
89
use Psr\Http\Message\RequestInterface;
10+
use Psr\Http\Message\ResponseInterface;
911

1012
/**
1113
* HTTP adapter mock
@@ -17,6 +19,7 @@
1719
class MockHttpAdapter implements HttpAdapter
1820
{
1921
private $requests = [];
22+
private $responses = [];
2023
private $exception;
2124

2225
/**
@@ -30,7 +33,12 @@ public function sendRequest(RequestInterface $request, array $options = [])
3033
throw $this->exception;
3134
}
3235

33-
return new Response();
36+
if (count($this->responses) > 0) {
37+
return array_shift($this->responses);
38+
}
39+
40+
return MessageFactoryDiscovery::find()->createResponse();
41+
3442
}
3543

3644
/**
@@ -61,6 +69,11 @@ public function setException(\Exception $exception)
6169
$this->exception = $exception;
6270
}
6371

72+
public function addResponse(ResponseInterface $response)
73+
{
74+
$this->responses[] = $response;
75+
}
76+
6477
/**
6578
* {@inheritdoc}
6679
*

tests/Functional/Varnish/VarnishProxyClientTest.php

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -121,10 +121,10 @@ public function testRefresh()
121121
$this->getProxyClient()->refresh('/cache.php')->flush();
122122
usleep(1000);
123123
$refreshed = $this->getResponse('/cache.php');
124-
124+
125125
$originalTimestamp = (float)(string) $response->getBody();
126126
$refreshedTimestamp = (float)(string) $refreshed->getBody();
127-
127+
128128
$this->assertGreaterThan($originalTimestamp, $refreshedTimestamp);
129129
}
130130

tests/Unit/CacheInvalidatorTest.php

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -197,10 +197,7 @@ public function testProxyClientExceptionsAreLogged()
197197
->shouldReceive('getStatusCode')->andReturn(403)
198198
->shouldReceive('getReasonPhrase')->andReturn('Forbidden')
199199
->getMock();
200-
$adapterException = new HttpAdapterException('Forbidden');
201-
$adapterException->setRequest($failedRequest);
202-
$adapterException->setResponse($response);
203-
$responseException = ProxyResponseException::proxyResponse($adapterException);
200+
$responseException = ProxyResponseException::proxyResponse($response);
204201

205202
$exceptions = new ExceptionCollection();
206203
$exceptions->add($unreachableException)->add($responseException);
@@ -221,7 +218,7 @@ public function testProxyClientExceptionsAreLogged()
221218
->shouldReceive('log')->once()
222219
->with(
223220
'critical',
224-
'403 error response "Forbidden" from caching proxy at 127.0.0.1',
221+
'403 error response "Forbidden" from caching proxy',
225222
['exception' => $responseException]
226223
)
227224
->getMock();

tests/Unit/ProxyClient/AbstractProxyClientTest.php

Lines changed: 32 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
use FOS\HttpCache\ProxyClient\Varnish;
1616
use FOS\HttpCache\Test\HttpClient\MockHttpAdapter;
1717
use Http\Adapter\Exception\HttpAdapterException;
18+
use Http\Discovery\MessageFactoryDiscovery;
1819
use Psr\Http\Message\RequestInterface;
1920
use \Mockery;
2021

@@ -31,7 +32,7 @@ class AbstractProxyClientTest extends \PHPUnit_Framework_TestCase
3132
/**
3233
* @dataProvider exceptionProvider
3334
*
34-
* @param \Exception $exception The exception that curl should throw.
35+
* @param \Exception $exception Exception thrown by HTTP adapter.
3536
* @param string $type The returned exception class to be expected.
3637
* @param string $message Optional exception message to match against.
3738
*/
@@ -74,30 +75,36 @@ public function exceptionProvider()
7475
$unreachableException = new HttpAdapterException();
7576
$unreachableException->setRequest($request);
7677

77-
// Client exception (with response)
78-
$response = \Mockery::mock('\Psr\Http\Message\ResponseInterface')
79-
->shouldReceive('getStatusCode')->andReturn(500)
80-
->shouldReceive('getReasonPhrase')->andReturn('Uh-oh!')
81-
->getMock()
82-
;
83-
$responseException = new HttpAdapterException();
84-
$responseException->setRequest($request);
85-
$responseException->setResponse($response);
86-
8778
return [
8879
[
8980
$unreachableException,
9081
'\FOS\HttpCache\Exception\ProxyUnreachableException',
9182
'bla.com'
92-
],
93-
[
94-
$responseException,
95-
'\FOS\HttpCache\Exception\ProxyResponseException',
96-
'bla.com'
97-
],
83+
]
9884
];
9985
}
10086

87+
public function testErrorResponsesAreConvertedToExceptions()
88+
{
89+
$response = MessageFactoryDiscovery::find()->createResponse(
90+
405,
91+
'Not allowed'
92+
);
93+
$this->client->addResponse($response);
94+
95+
$varnish = new Varnish(['127.0.0.1:123'], 'my_hostname.dev', $this->client);
96+
try {
97+
$varnish->purge('/')->flush();
98+
$this->fail('Should have aborted with an exception');
99+
} catch (ExceptionCollection $exceptions) {
100+
$this->assertCount(1, $exceptions);
101+
$this->assertEquals(
102+
'405 error response "Not allowed" from caching proxy',
103+
$exceptions->getFirst()->getMessage()
104+
);
105+
}
106+
}
107+
101108
/**
102109
* @expectedException \FOS\HttpCache\Exception\MissingHostException
103110
* @expectedExceptionMessage cannot be invalidated without a host
@@ -170,23 +177,23 @@ public function testFlushEmpty()
170177

171178
public function testFlushCountSuccess()
172179
{
173-
$self = $this;
174180
$httpAdapter = \Mockery::mock('\Http\Adapter\HttpAdapter')
175181
->shouldReceive('sendRequests')
176182
->once()
177183
->with(
178184
\Mockery::on(
179-
function ($requests) use ($self) {
185+
function ($requests) {
180186
/** @type RequestInterface[] $requests */
181-
$self->assertCount(4, $requests);
187+
$this->assertCount(4, $requests);
182188
foreach ($requests as $request) {
183-
$self->assertEquals('PURGE', $request->getMethod());
189+
$this->assertEquals('PURGE', $request->getMethod());
184190
}
185191

186192
return true;
187193
}
188194
)
189195
)
196+
->andReturn([])
190197
->getMock();
191198

192199
$varnish = new Varnish(['127.0.0.1', '127.0.0.2'], 'fos.lo', $httpAdapter);
@@ -202,23 +209,23 @@ function ($requests) use ($self) {
202209

203210
public function testEliminateDuplicates()
204211
{
205-
$self = $this;
206212
$client = \Mockery::mock('\Http\Adapter\HttpAdapter')
207213
->shouldReceive('sendRequests')
208214
->once()
209215
->with(
210216
\Mockery::on(
211-
function ($requests) use ($self) {
217+
function ($requests) {
212218
/** @type RequestInterface[] $requests */
213-
$self->assertCount(4, $requests);
219+
$this->assertCount(4, $requests);
214220
foreach ($requests as $request) {
215-
$self->assertEquals('PURGE', $request->getMethod());
221+
$this->assertEquals('PURGE', $request->getMethod());
216222
}
217223

218224
return true;
219225
}
220226
)
221227
)
228+
->andReturn([])
222229
->getMock();
223230

224231
$varnish = new Varnish(array('127.0.0.1', '127.0.0.2'), 'fos.lo', $client);

0 commit comments

Comments
 (0)