1212namespace FOS \HttpCache \ProxyClient ;
1313
1414use FOS \HttpCache \Exception \ExceptionCollection ;
15- use FOS \HttpCache \Exception \InvalidUrlException ;
16- use FOS \HttpCache \Exception \ProxyResponseException ;
1715use FOS \HttpCache \Exception \ProxyUnreachableException ;
18- use Guzzle \Http \Client ;
19- use Guzzle \Http \ClientInterface ;
20- use Guzzle \Http \Exception \CurlException ;
21- use Guzzle \Common \Exception \ExceptionCollection as GuzzleExceptionCollection ;
22- use Guzzle \Http \Exception \RequestException ;
23- use Guzzle \Http \Message \RequestInterface ;
16+ use FOS \HttpCache \ProxyClient \Request \InvalidationRequest ;
17+ use FOS \HttpCache \ProxyClient \Request \RequestQueue ;
18+ use Ivory \HttpAdapter \HttpAdapterFactory ;
19+ use Ivory \HttpAdapter \HttpAdapterInterface ;
20+ use Ivory \HttpAdapter \MultiHttpAdapterException ;
2421
2522/**
26- * Guzzle-based abstract caching proxy client
23+ * Abstract caching proxy client
2724 *
2825 * @author David de Boer <[email protected] > 2926 */
3027abstract class AbstractProxyClient implements ProxyClientInterface
3128{
32- /**
33- * IP addresses/hostnames of all caching proxy servers
34- *
35- * @var array
36- */
37- private $ servers ;
38-
3929 /**
4030 * HTTP client
4131 *
42- * @var ClientInterface
32+ * @var HttpAdapterInterface
4333 */
44- private $ client ;
34+ private $ httpAdapter ;
4535
4636 /**
4737 * Request queue
4838 *
49- * @var array|RequestInterface[]
39+ * @var RequestQueue
5040 */
51- private $ queue ;
41+ protected $ queue ;
5242
5343 /**
5444 * Constructor
@@ -61,238 +51,59 @@ abstract class AbstractProxyClient implements ProxyClientInterface
6151 * requests (optional). This is required if
6252 * you purge and refresh paths instead of
6353 * absolute URLs.
64- * @param ClientInterface $client HTTP client (optional). If no HTTP client
65- * is supplied, a default one will be
66- * created.
54+ * @param HttpAdapterInterface $httpAdapter If no HTTP client is supplied, a
55+ * default one will be created.
6756 */
68- public function __construct (array $ servers , $ baseUrl = null , ClientInterface $ client = null )
69- {
70- $ this ->client = $ client ?: new Client ();
71- $ this ->setServers ($ servers );
72- $ this ->setBaseUrl ($ baseUrl );
73- }
74-
75- /**
76- * Set caching proxy servers
77- *
78- * @param array $servers Caching proxy proxy server hostnames or IP
79- * addresses, including port if not port 80.
80- * E.g. array('127.0.0.1:6081')
81- *
82- * @throws InvalidUrlException If server is invalid or contains URL
83- * parts other than scheme, host, port
84- */
85- public function setServers (array $ servers )
86- {
87- $ this ->servers = array ();
88- foreach ($ servers as $ server ) {
89- $ this ->servers [] = $ this ->filterUrl ($ server , array ('scheme ' , 'host ' , 'port ' ));
90- }
91- }
92-
93- /**
94- * Set application hostname, optionally including a base URL, for purge and
95- * refresh requests
96- *
97- * @param string $url Your application’s base URL or hostname
98- */
99- public function setBaseUrl ($ url )
100- {
101- if ($ url ) {
102- $ url = $ this ->filterUrl ($ url );
103- }
104-
105- $ this ->client ->setBaseUrl ($ url );
57+ public function __construct (
58+ array $ servers ,
59+ $ baseUrl = null ,
60+ HttpAdapterInterface $ httpAdapter = null
61+ ) {
62+ $ this ->httpAdapter = $ httpAdapter ?: HttpAdapterFactory::guess ();
63+ $ this ->initQueue ($ servers , $ baseUrl );
10664 }
10765
10866 /**
10967 * {@inheritdoc}
11068 */
11169 public function flush ()
11270 {
113- $ queue = $ this ->queue ;
114- if (0 === count ($ queue )) {
71+ if (0 === $ this ->queue ->count ()) {
11572 return 0 ;
11673 }
117-
118- $ this ->queue = array ();
119- $ this ->sendRequests ($ queue );
120-
121- return count ($ queue );
122- }
123-
124- /**
125- * Add a request to the queue
126- *
127- * @param string $method HTTP method
128- * @param string $url URL
129- * @param array $headers HTTP headers
130- */
131- protected function queueRequest ($ method , $ url , array $ headers = array ())
132- {
133- $ signature = $ this ->getSignature ($ method , $ url , $ headers );
134- if (!isset ($ this ->queue [$ signature ])) {
135- $ this ->queue [$ signature ] = $ this ->createRequest ($ method , $ url , $ headers );
136- }
137- }
138-
139- /**
140- * Calculate a unique hash for the request, based on all significant information.
141- *
142- * @param string $method HTTP method
143- * @param string $url URL
144- * @param array $headers HTTP headers
145- *
146- * @return string A hash value for this request.
147- */
148- private function getSignature ($ method , $ url , array $ headers )
149- {
150- ksort ($ headers );
151-
152- return md5 ($ method . "\n" . $ url . "\n" . var_export ($ headers , true ));
153- }
154-
155- /**
156- * Create request
157- *
158- * @param string $method HTTP method
159- * @param string $url URL
160- * @param array $headers HTTP headers
161- *
162- * @return RequestInterface
163- */
164- protected function createRequest ($ method , $ url , array $ headers = array ())
165- {
166- return $ this ->client ->createRequest ($ method , $ url , $ headers );
167- }
168-
169- /**
170- * Sends all requests to each caching proxy server
171- *
172- * Requests are sent in parallel to minimise impact on performance.
173- *
174- * @param RequestInterface[] $requests Requests
175- *
176- * @throws ExceptionCollection
177- */
178- private function sendRequests (array $ requests )
179- {
180- $ allRequests = array ();
181-
182- foreach ($ requests as $ request ) {
183- $ headers = $ request ->getHeaders ()->toArray ();
184- // Force to re-create Host header if empty, as Apache chokes on this. See #128 for discussion.
185- if (empty ($ headers ['Host ' ])) {
186- unset( $ headers ['Host ' ] );
187- }
188- foreach ($ this ->servers as $ server ) {
189- $ proxyRequest = $ this ->client ->createRequest (
190- $ request ->getMethod (),
191- $ server . $ request ->getResource (),
192- $ headers
193- );
194- $ allRequests [] = $ proxyRequest ;
195- }
196- }
74+
75+ $ queue = clone $ this ->queue ;
76+ $ this ->queue ->clear ();
19777
19878 try {
199- $ this ->client ->send ($ allRequests );
200- } catch (GuzzleExceptionCollection $ e ) {
201- $ this ->handleException ($ e );
202- }
203- }
204-
205- /**
206- * Handle request exception
207- *
208- * @param GuzzleExceptionCollection $exceptions
209- *
210- * @throws ExceptionCollection
211- */
212- protected function handleException (GuzzleExceptionCollection $ exceptions )
213- {
214- $ collection = new ExceptionCollection ();
215-
216- foreach ($ exceptions as $ exception ) {
217- if ($ exception instanceof CurlException) {
218- // Caching proxy unreachable
219- $ e = ProxyUnreachableException::proxyUnreachable (
220- $ exception ->getRequest ()->getHost (),
221- $ exception ->getMessage (),
222- $ exception ->getRequest ()->getRawHeaders (),
223- $ exception
224- );
225- } elseif ($ exception instanceof RequestException) {
226- // Other error
227- $ e = ProxyResponseException::proxyResponse (
228- $ exception ->getRequest ()->getHost (),
229- $ exception ->getCode (),
230- $ exception ->getMessage (),
231- $ exception ->getRequest ()->getRawHeaders (),
232- $ exception
79+ $ responses = $ this ->httpAdapter ->sendRequests ($ queue ->all ());
80+ } catch (MultiHttpAdapterException $ e ) {
81+ $ collection = new ExceptionCollection ();
82+ foreach ($ e ->getExceptions () as $ exception ) {
83+ $ collection ->add (
84+ ProxyUnreachableException::proxyUnreachable (
85+ $ exception ->getRequest ()->getHeader ('Host ' ),
86+ $ exception ->getMessage (),
87+ null ,
88+ $ exception
89+ )
23390 );
234- } else {
235- // Unexpected exception type
236- $ e = $ exception ;
23791 }
23892
239- $ collection-> add ( $ e ) ;
93+ throw $ collection ;
24094 }
241-
242- throw $ collection ;
95+
96+ return count ( $ responses ) ;
24397 }
244-
245- /**
246- * Filter a URL
247- *
248- * Prefix the URL with "http://" if it has no scheme, then check the URL
249- * for validity. You can specify what parts of the URL are allowed.
250- *
251- * @param string $url
252- * @param string[] $allowedParts Array of allowed URL parts (optional)
253- *
254- * @throws InvalidUrlException If URL is invalid, the scheme is not http or
255- * contains parts that are not expected.
256- *
257- * @return string The URL (with default scheme if there was no scheme)
258- */
259- protected function filterUrl ($ url , array $ allowedParts = array ())
98+
99+ protected function queueRequest ($ method , $ url , array $ headers = array ())
260100 {
261- // parse_url doesn’t work properly when no scheme is supplied, so
262- // prefix URL with HTTP scheme if necessary.
263- if (false === strpos ($ url , ':// ' )) {
264- $ url = sprintf ('%s://%s ' , $ this ->getDefaultScheme (), $ url );
265- }
266-
267- if (!$ parts = parse_url ($ url )) {
268- throw InvalidUrlException::invalidUrl ($ url );
269- }
270- if (empty ($ parts ['scheme ' ])) {
271- throw InvalidUrlException::invalidUrl ($ url , 'empty scheme ' );
272- }
273-
274- if (!in_array (strtolower ($ parts ['scheme ' ]), $ this ->getAllowedSchemes ())) {
275- throw InvalidUrlException::invalidUrlScheme ($ url , $ parts ['scheme ' ], $ this ->getAllowedSchemes ());
276- }
277-
278- if (count ($ allowedParts ) > 0 ) {
279- $ diff = array_diff (array_keys ($ parts ), $ allowedParts );
280- if (count ($ diff ) > 0 ) {
281- throw InvalidUrlException::invalidUrlParts ($ url , $ allowedParts );
282- }
283- }
284-
285- return $ url ;
101+ $ this ->queue ->add (new InvalidationRequest ($ method , $ url , $ headers ));
286102 }
287-
288- /**
289- * Get default scheme
290- *
291- * @return string
292- */
293- protected function getDefaultScheme ()
103+
104+ protected function initQueue (array $ servers , $ baseUrl )
294105 {
295- return ' http ' ;
106+ $ this -> queue = new RequestQueue ( $ servers , $ baseUrl ) ;
296107 }
297108
298109 /**
0 commit comments