Permalink
Browse files

Merge pull request #17 from Hollywood-com/master

Varnish helper to use cUrl instead of fopen for requests
  • Loading branch information...
2 parents d58b251 + 58b32a2 commit 77711857c460e1dc1640aea0b2280efbe5b7b4d0 @dbu dbu committed Oct 23, 2012
Showing with 91 additions and 32 deletions.
  1. +8 −1 DependencyInjection/LiipCacheControlExtension.php
  2. +76 −30 Helper/Varnish.php
  3. +1 −1 README.md
  4. +6 −0 composer.json
@@ -8,7 +8,8 @@
Symfony\Component\DependencyInjection\Loader\XmlFileLoader,
Symfony\Component\DependencyInjection\ContainerBuilder,
Symfony\Component\DependencyInjection\Reference,
- Symfony\Component\DependencyInjection\DefinitionDecorator;
+ Symfony\Component\DependencyInjection\DefinitionDecorator,
+ Symfony\Component\DependencyInjection\Exception\RuntimeException;
class LiipCacheControlExtension extends Extension
{
@@ -46,6 +47,12 @@ public function load(array $configs, ContainerBuilder $container)
}
if (!empty($config['varnish'])) {
+
+ if (!extension_loaded('curl')) {
+ throw new RuntimeException('Varnish Helper requires cUrl php extension. Please install it to continue');
+
+ }
+
$loader->load('varnish_helper.xml');
$container->setParameter($this->getAlias().'.varnish.ips', $config['varnish']['ips']);
$container->setParameter($this->getAlias().'.varnish.domain', $config['varnish']['domain']);
View
@@ -6,7 +6,7 @@
* Helper to invalidate or force a refresh varnish entries
*
* Supports multiple varnish instances.
- *
+ *
* For invalidation uses PURGE requests to the frontend.
* See http://www.varnish-cache.org/trac/wiki/VCLExamplePurging
*
@@ -15,7 +15,7 @@
* netcat localhost 6081 << EOF
* PURGE /url/to/purge HTTP/1.1
* Host: webapp-host.name
- *
+ *
* EOF
*
* For a forced refresh it uses a normal GET with appropriate cache headers
@@ -40,12 +40,15 @@ class Varnish
private $domain;
private $port;
+ private $lastRequestError;
+ private $lastRequestInfo;
+
/**
* Constructor
*
* @param string $domain the domain we want to purge urls from. only domain and port are used, path is ignored
- * @param array $ips space separated list of varnish ips to talk to
- * @param int $port the port the varnishes listen on (its the same port for all instances)
+ * @param array $ips space separated list of varnish ips to talk to
+ * @param int $port the port the varnishes listen on (its the same port for all instances)
*/
public function __construct($domain, array $ips, $port)
{
@@ -54,63 +57,106 @@ public function __construct($domain, array $ips, $port)
if (isset($url['port'])) {
$this->domain .= ':' . $url['port'];
}
- $this->ips = $ips;
+ $this->ips = $ips;
$this->port = $port;
+
}
/**
* Purge this absolute path at all registered cache server
*
- * @param string $path Must be an absolute path
+ * @param string $path Must be an absolute path
+ * @param array $options Options for cUrl Request
+ *
+ * @return array An associative array with keys 'headers' and 'body' which holds a raw response from the server
+ *
* @throws \RuntimeException if connection to one of the varnish servers fails.
*/
- public function invalidatePath($path)
+ public function invalidatePath($path, array $options = array())
{
- $request = "PURGE $path HTTP/1.0\r\n";
- $request.= "Host: {$this->domain}\r\n";
- $request.= "Connection: Close\r\n\r\n";
+ //Garanteed to be a purge request
+ $options[CURLOPT_CUSTOMREQUEST] = 'PURGE';
+
+ $request = array('path' => $path);
- $this->sendRequestToAllVarnishes($request);
+ return $this->sendRequestToAllVarnishes($request, $options);
}
/**
- * Force this absolute path to be refreshed
+ * Force this absolute path to be refreshed
*
- * @param string $path Must be an absolute path
+ * @param string $path Must be an absolute path
+ * @param array $options Options for cUrl Request
+ *
+ * @return array An associative array with keys 'headers' and 'body' which holds a raw response from the server
* @throws \RuntimeException if connection to one of the varnish servers fails.
*/
- public function refreshPath($path)
+ public function refreshPath($path, array $options = array())
{
- $request = "GET $path HTTP/1.0\r\n";
- $request.= "Host: {$this->domain}\r\n";
- $request.= "Cache-Control: no-cache, no-store, max-age=0, must-revalidate";
- $request.= "Connection: Close\r\n\r\n";
- $this->sendRequestToAllVarnishes($request);
+ $headers = array("Cache-Control: no-cache, no-store, max-age=0, must-revalidate");
+
+ $options[CURLOPT_HTTPHEADER] = $headers;
+ $options[CURLOPT_CUSTOMREQUEST] = 'GET';
+
+ $request = array('path' => $path);
+
+ return $this->sendRequestToAllVarnishes($request, $options);
}
/**
* Send a request to all configured varnishes
*
- * @param string $request request string
+ * @param array $request request string
+ * @param array $options Options for request
+ *
+ * @return array An associative array with keys 'headers', 'body', 'error' and 'errorNumber' for each configured Ip
* @throws \RuntimeException if connection to one of the varnish servers fails. TODO: should we be more tolerant?
*/
- protected function sendRequestToAllVarnishes($request)
+ protected function sendRequestToAllVarnishes($request, array $options = array())
{
+
+ $requestResponseByIp = array();
+
+ $curlHandler = curl_init($this->domain);
+
+ foreach ($options as $option => $value) {
+
+ curl_setopt($curlHandler, (int) $option, $value);
+ }
+ //Default Options
+ curl_setopt($curlHandler, CURLOPT_RETURNTRANSFER, true);
+ curl_setopt($curlHandler, CURLOPT_HEADER, true); // Display headers
+
foreach ($this->ips as $ip) {
- $fp = fsockopen($ip, $this->port, $errno, $errstr, 2);
- if (!$fp) {
- throw new \RuntimeException("$errstr ($errno)");
- }
- fwrite($fp, $request);
+ curl_setopt($curlHandler, CURLOPT_URL, $ip.':'.$this->port.$request['path']);
- // read answer to the end, to be sure varnish is finished before continuing
- while (!feof($fp)) {
- fgets($fp, 128);
+ $response = curl_exec($curlHandler);
+
+ //Failed
+ if ($response === false) {
+ $header = '';
+ $body = '';
+ $error = curl_error($curlHandler);
+ $errorNumber = curl_errno($curlHandler);
+
+ } else {
+ $error = null;
+ $errorNumber = CURLE_OK;
+ list($header, $body) = explode("\r\n\r\n", $response, 2);
}
- fclose($fp);
+ $requestResponseByIp[$ip] = array('headers' => $header,
+ 'body' => $body,
+ 'error' => $error,
+ 'errorNumber' => $errorNumber);
+
}
+
+ curl_close($curlHandler);
+
+ return $requestResponseByIp;
}
+
}
View
@@ -4,7 +4,7 @@ CacheControlBundle
This Bundle provides a way to set path based cache expiration headers via the app configuration and provides
a helper to control the reverse proxy varnish.
-This bundle works with Symfony 2.0 as well as the upcoming release 2.1.
+This bundle works with Symfony 2.0 and 2.1.
[![Build Status](https://secure.travis-ci.org/liip/LiipCacheControlBundle.png)](http://travis-ci.org/liip/LiipCacheControlBundle)
View
@@ -5,6 +5,9 @@
"keywords": ["esi", "varnish", "caching", "http"],
"license": "MIT",
"minimum-stability": "dev",
+ "require-dev": {
+ "ext-curl": "*"
+ },
"authors": [
{
"name": "Liip AG",
@@ -22,5 +25,8 @@
"autoload": {
"psr-0": { "Liip\\CacheControlBundle": "" }
},
+ "suggest": {
+ "ext-curl": "Used by Varnish Helper to construct requests."
+ },
"target-dir": "Liip/CacheControlBundle"
}

0 comments on commit 7771185

Please sign in to comment.