You are viewing a plain text version of this content. The canonical link for it is here.
Posted to dev@shindig.apache.org by ch...@apache.org on 2009/02/17 12:53:06 UTC

svn commit: r744970 - in /incubator/shindig/trunk/php/src: common/ common/sample/ gadgets/

Author: chabotc
Date: Tue Feb 17 11:53:06 2009
New Revision: 744970

URL: http://svn.apache.org/viewvc?rev=744970&view=rev
Log:
Changed the caching expiration policy from a expiration on get to a TTL on set, and modified the RemoteContent fetchers to use the Cache-Control: max-age or Expiration headers to determine the TTL for caching them

Modified:
    incubator/shindig/trunk/php/src/common/Cache.php
    incubator/shindig/trunk/php/src/common/RemoteContent.php
    incubator/shindig/trunk/php/src/common/RemoteContentFetcher.php
    incubator/shindig/trunk/php/src/common/RemoteContentRequest.php
    incubator/shindig/trunk/php/src/common/sample/BasicRemoteContent.php
    incubator/shindig/trunk/php/src/common/sample/BasicRemoteContentFetcher.php
    incubator/shindig/trunk/php/src/common/sample/CacheApc.php
    incubator/shindig/trunk/php/src/common/sample/CacheFile.php
    incubator/shindig/trunk/php/src/common/sample/CacheMemcache.php
    incubator/shindig/trunk/php/src/gadgets/SigningFetcher.php

Modified: incubator/shindig/trunk/php/src/common/Cache.php
URL: http://svn.apache.org/viewvc/incubator/shindig/trunk/php/src/common/Cache.php?rev=744970&r1=744969&r2=744970&view=diff
==============================================================================
--- incubator/shindig/trunk/php/src/common/Cache.php (original)
+++ incubator/shindig/trunk/php/src/common/Cache.php Tue Feb 17 11:53:06 2009
@@ -24,9 +24,9 @@
 
 abstract class Cache {
 
-  abstract function get($key, $expiration = false);
+  abstract function get($key);
 
-  abstract function set($key, $value);
+  abstract function set($key, $value, $ttl = false);
 
   abstract function delete($key);
 }
\ No newline at end of file

Modified: incubator/shindig/trunk/php/src/common/RemoteContent.php
URL: http://svn.apache.org/viewvc/incubator/shindig/trunk/php/src/common/RemoteContent.php?rev=744970&r1=744969&r2=744970&view=diff
==============================================================================
--- incubator/shindig/trunk/php/src/common/RemoteContent.php (original)
+++ incubator/shindig/trunk/php/src/common/RemoteContent.php Tue Feb 17 11:53:06 2009
@@ -21,8 +21,8 @@
 
 /*
  * remoteContent* classes, we departed from the shindig java base style a bit here
- * We want to use curl_multi for our content fetching because we don't have any fancy 
- * worker queue's where the java variant does. 
+ * We want to use curl_multi for our content fetching because we don't have any fancy
+ * worker queue's where the java variant does.
  * So a different methodlogy which calls for a different working unfortunatly, however
  * it's kept in the spirit of the java variant as much as possible
  */
@@ -32,7 +32,7 @@
 
 abstract class RemoteContent {
 
-  abstract public function fetch($request, $context);
+  abstract public function fetch(RemoteContentRequest $request, $context);
 
   abstract public function multiFetch(Array $requests, Array $contexts);
 }

Modified: incubator/shindig/trunk/php/src/common/RemoteContentFetcher.php
URL: http://svn.apache.org/viewvc/incubator/shindig/trunk/php/src/common/RemoteContentFetcher.php?rev=744970&r1=744969&r2=744970&view=diff
==============================================================================
--- incubator/shindig/trunk/php/src/common/RemoteContentFetcher.php (original)
+++ incubator/shindig/trunk/php/src/common/RemoteContentFetcher.php Tue Feb 17 11:53:06 2009
@@ -20,14 +20,14 @@
 
 
 abstract class RemoteContentFetcher {
-  
+
   protected $fetcher;
 
   protected function setNextFetcher($fetcher = null) {
     $this->fetcher = $fetcher;
   }
 
-  abstract public function fetchRequest($request);
+  abstract public function fetchRequest(RemoteContentRequest $request);
 
   abstract public function multiFetchRequest(Array $requests);
 

Modified: incubator/shindig/trunk/php/src/common/RemoteContentRequest.php
URL: http://svn.apache.org/viewvc/incubator/shindig/trunk/php/src/common/RemoteContentRequest.php?rev=744970&r1=744969&r2=744970&view=diff
==============================================================================
--- incubator/shindig/trunk/php/src/common/RemoteContentRequest.php (original)
+++ incubator/shindig/trunk/php/src/common/RemoteContentRequest.php Tue Feb 17 11:53:06 2009
@@ -67,7 +67,7 @@
       }
       // Bypass caching in proxies as well.
       if (! $setPragmaHeader && $options->ignoreCache) {
-        $tmpHeaders .= "Pragma:no-cache\n";
+        $tmpHeaders .= "Pragma: no-cache\n";
       }
       $this->headers = $tmpHeaders;
     }
@@ -85,22 +85,6 @@
   }
 
   /**
-   * Creates a new request to a different URL using all request data from
-   * an existing request.
-   *
-   * @param uri
-   * @param base The base request to copy data from.
-   */
-  public static function createRemoteContentRequestWithUriBase($uri, $base) {
-    $this->uri = $uri;
-    $this->method = $base->method;
-    $this->options = $base->options;
-    $this->headers = $base->headers;
-    $this->contentType = $base->contentType;
-    $this->postBody = $base->postBody;
-  }
-
-  /**
    * Basic GET request.
    *
    * @param uri
@@ -110,75 +94,6 @@
   }
 
   /**
-   * GET with options
-   *
-   * @param uri
-   * @param options
-   */
-  public function createRemoteContentRequestWithUriOptions($uri, $options) {
-    $this->createRemoteContentRequest("GET", $uri, null, null, $options);
-  }
-
-  /**
-   * GET request with custom headers and default options
-   * @param uri
-   * @param headers
-   */
-  public function RemoteContentRequestWithUriHeaders($uri, $headers) {
-    $this->createRemoteContentRequest("GET", $uri, $headers, null, RemoteContentRequest::getDefaultOptions());
-  }
-
-  /**
-   * GET request with custom headers + options
-   * @param uri
-   * @param headers
-   * @param options
-   */
-  public function createRemoteContentRequestWithUriHeadersOptions($uri, $headers, $options) {
-    $this->createRemoteContentRequest("GET", $uri, $headers, null, $options);
-  }
-
-  /**
-   * Basic POST request
-   * @param uri
-   * @param postBody
-   */
-  public function RemoteContentRequestWithUriPostBody($uri, $postBody) {
-    $this->createRemoteContentRequest("POST", $uri, null, $postBody, RemoteContentRequest::getDefaultOptions());
-  }
-
-  /**
-   * POST request with options
-   * @param uri
-   * @param postBody
-   * @param options
-   */
-  public function createRemoteContentRequestWithUriPostBodyOptions($uri, $postBody, $options) {
-    $this->createRemoteContentRequest("POST", $uri, null, $postBody, $options);
-  }
-
-  /**
-   * POST request with headers
-   * @param uri
-   * @param headers
-   * @param postBody
-   */
-  public function createRemoteContentRequestWithUriHeadersPostBody($uri, $headers, $postBody) {
-    $this->createRemoteContentRequest("POST", $uri, $headers, $postBody, RemoteContentRequest::getDefaultOptions());
-  }
-
-  /**
-   * POST request with options + headers
-   * @param uri
-   * @param headers
-   * @param postBody
-   * @param options
-   */
-  public function createRemoteContentRequestWithUriHeadersPostBodyOptions($uri, $headers, $postBody, $options) {
-    $this->createRemoteContentRequest("POST", $uri, $headers, $postBody, $options);
-  }
-
-  /**
    * Creates a simple GET request
    *
    * @param uri
@@ -190,23 +105,6 @@
     return $this->createRemoteContentRequestWithUriOptions($uri, $options);
   }
 
-  /**
-   * Simple constructor for setting a basic response from a string. Mostly used
-   * for testing.
-   *
-   * @param body
-   */
-  public function getHttpFalseResponseBody($body) {
-    return $this->createFalseResponse(RemoteContentRequest::$SC_OK, $body, null);
-  }
-
-  private function createFalseResponse($httpCode, $body, $headers) {
-    $this->httpCode = $httpCode;
-    $this->responseContent = $body;
-    $this->headers = $headers;
-    return $this;
-  }
-
   // returns a hash code which identifies this request, used for caching
   // takes url and postbody into account for constructing the sha1 checksum
   public function toHash() {
@@ -307,17 +205,8 @@
     return null;
   }
 
-  //FIXME: Find a better way to do this
-  // The headers can be an array of elements.
   public function getResponseHeader($headerName) {
-    $headers = explode("\n", $this->responseHeaders);
-    foreach ($headers as $header) {
-      $key = explode(":", $header, 2);
-      if ($key[0] == $headerName) {
-        return trim($key[1]);
-      }
-    }
-    return null;
+    return isset($this->responseHeaders[$headerName]) ? $this->responseHeaders[$headerName] : null;
   }
 
   public function getCreated() {

Modified: incubator/shindig/trunk/php/src/common/sample/BasicRemoteContent.php
URL: http://svn.apache.org/viewvc/incubator/shindig/trunk/php/src/common/sample/BasicRemoteContent.php?rev=744970&r1=744969&r2=744970&view=diff
==============================================================================
--- incubator/shindig/trunk/php/src/common/sample/BasicRemoteContent.php (original)
+++ incubator/shindig/trunk/php/src/common/sample/BasicRemoteContent.php Tue Feb 17 11:53:06 2009
@@ -18,60 +18,78 @@
  * under the License.
  */
 
-
 class BasicRemoteContent extends RemoteContent {
 
-  public function fetch($request, $context) {
+  public function fetch(RemoteContentRequest $request, $context) {
     $cache = Config::get('data_cache');
     $cache = new $cache();
     $remoteContentFetcher = new BasicRemoteContentFetcher();
-    if (! ($request instanceof RemoteContentRequest)) {
-      throw new RemoteContentException("Invalid request type in remoteContent");
-    }
-    // determine which requests we can load from cache, and which we have to actually fetch
-    if (! $context->getIgnoreCache() && ! $request->isPost() && ($cachedRequest = $cache->get($request->toHash(), $context->getRefreshInterval())) !== false) {
-      $ret = $cachedRequest;
+    if (! $context->getIgnoreCache() && ! $request->isPost() && ($cachedRequest = $cache->get($request->toHash())) !== false) {
+      $request = $cachedRequest;
     } else {
-      $ret = $remoteContentFetcher->fetchRequest($request);
-      // only cache requests that returned a 200 OK and is not a POST
-      if ($request->getHttpCode() == '200' && ! $request->isPost()) {
-        $cache->set($request->toHash(), $request);
-      }
+      $request = $remoteContentFetcher->fetchRequest($request);
+      $this->setRequestCache($request, $cache, $context);
     }
-    return $ret;
+    return $request;
   }
 
   public function multiFetch(Array $requests, Array $contexts) {
     $cache = Config::get('data_cache');
     $cache = new $cache();
     $remoteContentFetcher = new BasicRemoteContentFetcher();
-    
     $rets = array();
     $requestsToProc = array();
-    
     foreach ($requests as $request) {
       list(, $context) = each($contexts);
       if (! ($request instanceof RemoteContentRequest)) {
         throw new RemoteContentException("Invalid request type in remoteContent");
       }
       // determine which requests we can load from cache, and which we have to actually fetch
-      if (! $context->getIgnoreCache() && ! $request->isPost() && ($cachedRequest = $cache->get($request->toHash(), $context->getRefreshInterval())) !== false) {
+      if (! $context->getIgnoreCache() && ! $request->isPost() && ($cachedRequest = $cache->get($request->toHash())) !== false) {
         $rets[] = $cachedRequest;
       } else {
         $requestsToProc[] = $request;
       }
     }
-    
     $newRets = $remoteContentFetcher->multiFetchRequest($requestsToProc);
-    
     foreach ($newRets as $request) {
-      // only cache requests that returned a 200 OK and is not a POST
-      if ($request->getHttpCode() == '200' && ! $request->isPost()) {
-        $cache->set($request->toHash(), $request);
-      }
+      $this->setRequestCache($request, $cache, $context);
       $rets[] = $request;
     }
-    
     return $rets;
   }
+
+  private function setRequestCache(RemoteContentRequest $request, Cache $cache, GadgetContext $context) {
+    if (! $request->isPost() && ! $context->getIgnoreCache()) {
+      $ttl = Config::get('cache_time');
+      if ($request->getHttpCode() == '200') {
+        // Got a 200 OK response, calculate the TTL to use for caching it
+        if (($expires = $request->getResponseHeader('Expires')) != null) {
+          // prefer to use the servers notion of the time since there could be a clock-skew, but otherwise use our own
+          $date = $request->getResponseHeader('Date') != null ? $request->getResponseHeader('Date') : gmdate('D, d M Y H:i:s', $_SERVER['REQUEST_TIME']) . ' GMT';
+          // convert both dates to unix epoch seconds, and calculate the TTL
+          $date = strtotime($date);
+          $expires = strtotime($expires);
+          $ttl = $expires - $date;
+          // Don't fall for the old expiration-date-in-the-past trick, we *really* want to cache stuff since a large SNS's traffic would devastate a gadget developer's server
+          if ($expires - $date > 1) {
+            $ttl = $expires - $date;
+          }
+        }
+        // See http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html : The Cache-Control: max-age=<seconds> overrides the expires header, sp if both are present this one will overwrite the $ttl
+        if (($cacheControl = $request->getResponseHeader('Cache-Control')) != null) {
+          $bits = explode('=', $cacheControl);
+          foreach ($bits as $key => $val) {
+            if ($val == 'max-age' && isset($bits[$key + 1])) {
+              $ttl = $bits[$key + 1];
+              break;
+            }
+          }
+        }
+      } else {
+        $ttl = 5 * 60; // cache errors for 5 minutes, takes the denial of service attack type behaviour out of having an error :)
+      }
+      $cache->set($request->toHash(), $request, $ttl);
+    }
+  }
 }

Modified: incubator/shindig/trunk/php/src/common/sample/BasicRemoteContentFetcher.php
URL: http://svn.apache.org/viewvc/incubator/shindig/trunk/php/src/common/sample/BasicRemoteContentFetcher.php?rev=744970&r1=744969&r2=744970&view=diff
==============================================================================
--- incubator/shindig/trunk/php/src/common/sample/BasicRemoteContentFetcher.php (original)
+++ incubator/shindig/trunk/php/src/common/sample/BasicRemoteContentFetcher.php Tue Feb 17 11:53:06 2009
@@ -1,4 +1,5 @@
 <?php
+
 /**
  * Licensed to the Apache Software Foundation (ASF) under one
  * or more contributor license agreements.  See the NOTICE file
@@ -18,155 +19,151 @@
  * under the License.
  */
 
-
 /**
  * Basic remote content fetcher, uses curl_multi to fetch multiple resources at the same time
  */
 class BasicRemoteContentFetcher extends RemoteContentFetcher {
   private $requests = array();
-  const USER_AGENT = 'Shindig PHP';
+  private $disallowedHeaders = array('Keep-Alive', 'Host', 'Accept-Encoding', 'Set-Cookie', 'Content-Length', 'Content-Encoding', 'ETag', 'Last-Modified', 'Accept-Ranges', 'Vary', 'Expires', 'Date', 'Pragma', 'Cache-Control', 'Transfer-Encoding', 'If-Modified-Since');
+  const USER_AGENT = 'Apache Shindig';
 
-  public function fetchRequest($request) {
-    $request->handle = curl_init();
-    curl_setopt($request->handle, CURLOPT_URL, $request->getUrl());
-    curl_setopt($request->handle, CURLOPT_FOLLOWLOCATION, 1);
-    curl_setopt($request->handle, CURLOPT_RETURNTRANSFER, 1);
-    curl_setopt($request->handle, CURLOPT_AUTOREFERER, 1);
-    curl_setopt($request->handle, CURLOPT_MAXREDIRS, 10);
-    curl_setopt($request->handle, CURLOPT_CONNECTTIMEOUT, Config::get('curl_connection_timeout'));
-    curl_setopt($request->handle, CURLOPT_TIMEOUT, 2);
-    curl_setopt($request->handle, CURLOPT_HEADER, 1);
-    curl_setopt($request->handle, CURLOPT_SSL_VERIFYPEER, 0);
-    $proxy = Config::get('proxy');
-    if (! empty($proxy)) {
-      curl_setopt($request->handle, CURLOPT_PROXY, $proxy);
-    }
-    if ($request->hasHeaders()) {
-      $headers = explode("\n", $request->getHeaders());
-      $outHeaders = array();
-      foreach ($headers as $header) {
-        if (strpos($header, ':')) {
-          $key = trim(substr($header, 0, strpos($header, ':')));
-          $val = trim(substr($header, strpos($header, ':') + 1));
-          if (strcmp($key, "User-Agent") != 0 && strcasecmp($key, "Transfer-Encoding") != 0 && strcasecmp($key, "Cache-Control") != 0 && strcasecmp($key, "Expires") != 0 && strcasecmp($key, "Content-Length") != 0) {
-            $outHeaders[] = "$key: $val";
-          }
-        }
-      }
-      $outHeaders[] = "User-Agent: " . BasicRemoteContentFetcher::USER_AGENT;
-      curl_setopt($request->handle, CURLOPT_HTTPHEADER, $outHeaders);
-    }
-    if ($request->isPost()) {
-      curl_setopt($request->handle, CURLOPT_POST, 1);
-      curl_setopt($request->handle, CURLOPT_POSTFIELDS, $request->getPostBody());
-    }
+  /**
+   * Performs a single (RemoteContentRequest) request and fills in the response
+   * in the $request object
+   *
+   * @param RemoteContentRequest $request
+   * @return RemoteContentRequest $request
+   */
+  public function fetchRequest(RemoteContentRequest $request) {
+    $request->handle = $this->initCurlHandle($request->getUrl());
+    $this->setHeaders($request);
     // Execute the request
     $content = @curl_exec($request->handle);
-    $header = '';
-    $body = '';
-    // on redirects and such we get multiple headers back from curl it seems, we really only want the last one
-    while (substr($content, 0, strlen('HTTP')) == 'HTTP' && strpos($content, "\r\n\r\n") !== false) {
-      $header = substr($content, 0, strpos($content, "\r\n\r\n"));
-      $content = $body = substr($content, strlen($header) + 4);
-    }
-    $httpCode = curl_getinfo($request->handle, CURLINFO_HTTP_CODE);
-    $contentType = curl_getinfo($request->handle, CURLINFO_CONTENT_TYPE);
-    if (! $httpCode) {
-      $httpCode = '404';
-    }
-    $request->setHttpCode($httpCode);
-    $request->setContentType($contentType);
-    $request->setResponseHeaders($header);
-    $request->setResponseContent($body);
-    $request->setResponseSize(strlen($content));
+    $this->parseResult($request, $content);
     curl_close($request->handle);
     unset($request->handle);
     return $request;
   }
 
+  /**
+   * Performs multiple (array of RemoteContentRequest) requests and fills in the responses
+   * in the $request objects
+   *
+   * @param Array of RemoteContentRequest's $requests
+   * @return $requests
+   */
   public function multiFetchRequest(Array $requests) {
     $mh = curl_multi_init();
     foreach ($requests as $request) {
-      $request->handle = curl_init();
-      curl_setopt($request->handle, CURLOPT_URL, $request->getUrl());
-      curl_setopt($request->handle, CURLOPT_FOLLOWLOCATION, 1);
-      curl_setopt($request->handle, CURLOPT_RETURNTRANSFER, 1);
-      curl_setopt($request->handle, CURLOPT_AUTOREFERER, 1);
-      curl_setopt($request->handle, CURLOPT_MAXREDIRS, 10);
-      curl_setopt($request->handle, CURLOPT_CONNECTTIMEOUT, Config::get('curl_connection_timeout'));
-      curl_setopt($request->handle, CURLOPT_TIMEOUT, 2);
-      curl_setopt($request->handle, CURLOPT_HEADER, 1);
-      curl_setopt($request->handle, CURLOPT_SSL_VERIFYPEER, 0);
+      $request->handle = $this->initCurlHandle($request->getUrl());
       // Set this so the multihandler will return data
       curl_setopt($request->handle, CURLOPT_RETURNTRANSFER, 1);
-      
-      $proxy = Config::get('proxy');
-      if (! empty($proxy)) {
-        curl_setopt($request->handle, CURLOPT_PROXY, $proxy);
-      }
-      if ($request->hasHeaders()) {
-        $headers = explode("\n", $request->getHeaders());
-        $outHeaders = array();
-        foreach ($headers as $header) {
-          if (strpos($header, ':')) {
-            $key = trim(substr($header, 0, strpos($header, ':')));
-            $val = trim(substr($header, strpos($header, ':') + 1));
-            if (strcmp($key, "User-Agent") != 0 && strcasecmp($key, "Transfer-Encoding") != 0 && strcasecmp($key, "Cache-Control") != 0 && strcasecmp($key, "Expires") != 0 && strcasecmp($key, "Content-Length") != 0) {
-              $outHeaders[] = "$key: $val";
-            }
-          }
-        }
-        $outHeaders[] = "User-Agent: " . BasicRemoteContentFetcher::USER_AGENT;
-        curl_setopt($request->handle, CURLOPT_HTTPHEADER, $outHeaders);
-      }
-      if ($request->isPost()) {
-        curl_setopt($request->handle, CURLOPT_POST, 1);
-        curl_setopt($request->handle, CURLOPT_POSTFIELDS, $request->getPostBody());
-      }
+      $this->setHeaders($request);
       curl_multi_add_handle($mh, $request->handle);
     }
-    
     $running = null;
-    //execute the handles
     do {
       curl_multi_exec($mh, $running);
     } while ($running > 0);
-    
-    //Ideally this should be 0 after curl_multi_info_read() is call, meaning all
-    //calls have bee processed
-    $msg_queue = null;
-    $responses = curl_multi_info_read($mh, $msg_queue);
-    
     foreach ($requests as $request) {
       // Execute the request
       $content = curl_multi_getcontent($request->handle);
-      $header = '';
-      $body = '';
-      // on redirects and such we get multiple headers back from curl it seems, we really only want the last one
-      while (substr($content, 0, strlen('HTTP')) == 'HTTP' && strpos($content, "\r\n\r\n") !== false) {
-        $header = substr($content, 0, strpos($content, "\r\n\r\n"));
-        $content = $body = substr($content, strlen($header) + 4);
-      }
-      $httpCode = curl_getinfo($request->handle, CURLINFO_HTTP_CODE);
-      $contentType = curl_getinfo($request->handle, CURLINFO_CONTENT_TYPE);
-      if (! $httpCode) {
-        $httpCode = '404';
-      }
-      $request->setHttpCode($httpCode);
-      $request->setContentType($contentType);
-      $request->setResponseHeaders($header);
-      $request->setResponseContent($body);
-      $request->setResponseSize(strlen($content));
-    }
-    
-    //	close the handles
-    foreach ($requests as $request) {
-      curl_close($request->handle);
+      $this->parseResult($request, $content);
+      curl_multi_remove_handle($mh, $request->handle);
       unset($request->handle);
     }
     curl_multi_close($mh);
     unset($mh);
-    
     return $requests;
   }
+
+  /**
+   * Parses the result content into the headers and body, and retrieves the http code and content type
+   *
+   * @param RemoteContentRequest $request
+   * @param string $content
+   */
+  private function parseResult(RemoteContentRequest $request, $content) {
+    $headers = '';
+    $body = '';
+    // on redirects and such we get multiple headers back from curl it seems, we really only want the last one
+    while (substr($content, 0, strlen('HTTP')) == 'HTTP' && strpos($content, "\r\n\r\n") !== false) {
+      $headers = substr($content, 0, strpos($content, "\r\n\r\n"));
+      $content = $body = substr($content, strlen($headers) + 4);
+    }
+    $headers = explode("\n", $headers);
+    $parsedHeaders = array();
+    foreach ($headers as $header) {
+      if (strpos($header, ':')) {
+        $key = trim(substr($header, 0, strpos($header, ':')));
+        $key = str_replace(' ', '-', ucwords(str_replace('-', ' ', $key)));
+        $val = trim(substr($header, strpos($header, ':') + 1));
+        $parsedHeaders[$key] = $val;
+      }
+    }
+    $httpCode = curl_getinfo($request->handle, CURLINFO_HTTP_CODE);
+    $contentType = curl_getinfo($request->handle, CURLINFO_CONTENT_TYPE);
+    if (! $httpCode) {
+      $httpCode = '404';
+    }
+    $request->setHttpCode($httpCode);
+    $request->setContentType($contentType);
+    $request->setResponseHeaders($parsedHeaders);
+    $request->setResponseContent($body);
+    $request->setResponseSize(strlen($content));
+  }
+
+  /**
+   * Sets the headers and post body for the request if they are specified
+   *
+   * @param RemoteContentRequest $request
+   */
+  private function setHeaders(RemoteContentRequest $request) {
+    if ($request->hasHeaders()) {
+      $headers = explode("\n", $request->getHeaders());
+      $outHeaders = array();
+      foreach ($headers as $header) {
+        if (strpos($header, ':')) {
+          $key = trim(substr($header, 0, strpos($header, ':')));
+          $key = str_replace(' ', '-', ucwords(str_replace('-', ' ', $key)));
+          $val = trim(substr($header, strpos($header, ':') + 1));
+          if (! in_array($key, $this->disallowedHeaders)) {
+            $outHeaders[] = "$key: $val";
+          }
+        }
+      }
+      $outHeaders[] = "User-Agent: " . BasicRemoteContentFetcher::USER_AGENT;
+      curl_setopt($request->handle, CURLOPT_HTTPHEADER, $outHeaders);
+    }
+    if ($request->isPost()) {
+      curl_setopt($request->handle, CURLOPT_POST, 1);
+      curl_setopt($request->handle, CURLOPT_POSTFIELDS, $request->getPostBody());
+    }
+  }
+
+  /**
+   * Initializes a curl handle for making a request
+   * This will set the timeout based on the 'curl_connection_timeout configuration', and
+   * set a proxy server to use if the 'proxy' config string is not empty
+   *
+   * @param string $url
+   * @return curl handle
+   */
+  private function initCurlHandle($url) {
+    $handle = curl_init();
+    curl_setopt($handle, CURLOPT_URL, $url);
+    curl_setopt($handle, CURLOPT_FOLLOWLOCATION, 1);
+    curl_setopt($handle, CURLOPT_RETURNTRANSFER, 1);
+    curl_setopt($handle, CURLOPT_AUTOREFERER, 1);
+    curl_setopt($handle, CURLOPT_MAXREDIRS, 10);
+    curl_setopt($handle, CURLOPT_CONNECTTIMEOUT, Config::get('curl_connection_timeout'));
+    curl_setopt($handle, CURLOPT_TIMEOUT, 2);
+    curl_setopt($handle, CURLOPT_HEADER, 1);
+    curl_setopt($handle, CURLOPT_SSL_VERIFYPEER, 0);
+    $proxy = Config::get('proxy');
+    if (! empty($proxy)) {
+      curl_setopt($handle, CURLOPT_PROXY, $proxy);
+    }
+    return $handle;
+  }
 }

Modified: incubator/shindig/trunk/php/src/common/sample/CacheApc.php
URL: http://svn.apache.org/viewvc/incubator/shindig/trunk/php/src/common/sample/CacheApc.php?rev=744970&r1=744969&r2=744970&view=diff
==============================================================================
--- incubator/shindig/trunk/php/src/common/sample/CacheApc.php (original)
+++ incubator/shindig/trunk/php/src/common/sample/CacheApc.php Tue Feb 17 11:53:06 2009
@@ -50,40 +50,38 @@
   }
 
   private function waitForLock($key) {
-    // 20 x 250 = 5 seconds
-    $tries = 20;
+    // 10 x 100ms = 1 second
+    $tries = 10;
     $cnt = 0;
     do {
-      // 250 ms is a long time to sleep, but it does stop the server from burning all resources on polling locks..
-      usleep(250);
+      usleep(100);
       $cnt ++;
     } while ($cnt <= $tries && $this->isLocked());
     if ($this->isLocked()) {
-      // 5 seconds passed, assume the owning process died off and remove it
       $this->removeLock($key);
     }
   }
 
-  public function get($key, $expiration = false) {
-    if (! $expiration) {
-      // default to global cache time
-      $expiration = Config::Get('cache_time');
-    }
+  public function get($key) {
     if (($ret = @apc_fetch($key)) === false) {
       return false;
     }
-    if (time() - $ret['time'] > $expiration) {
-      $this->delete($key);
-      return false;
-    }
-    return unserialize($ret['data']);
+    return $ret;
   }
 
-  public function set($key, $value) {
-    // we store it with the cache_time default expiration so objects will atleast get cleaned eventually.
-    if (@apc_store($key, array('time' => time(), 'data' => serialize($value)), Config::Get('cache_time')) == false) {
+  public function set($key, $value, $ttl = false) {
+    if (! $ttl) {
+      $ttl = Config::Get('cache_time');
+    }
+    if ($this->isLocked($key)) {
+      $this->waitForLock($key);
+    }
+    $this->createLock($key);
+    if (@apc_store($key, $value, $ttl) == false) {
+      $this->removeLock($key);
       throw new CacheException("Couldn't store data in cache");
     }
+    $this->removeLock($key);
   }
 
   public function delete($key) {

Modified: incubator/shindig/trunk/php/src/common/sample/CacheFile.php
URL: http://svn.apache.org/viewvc/incubator/shindig/trunk/php/src/common/sample/CacheFile.php?rev=744970&r1=744969&r2=744970&view=diff
==============================================================================
--- incubator/shindig/trunk/php/src/common/sample/CacheFile.php (original)
+++ incubator/shindig/trunk/php/src/common/sample/CacheFile.php Tue Feb 17 11:53:06 2009
@@ -1,4 +1,5 @@
 <?php
+
 /**
  * Licensed to the Apache Software Foundation (ASF) under one
  * or more contributor license agreements.  See the NOTICE file
@@ -18,13 +19,12 @@
  * under the License.
  */
 
-
 /*
  * This class impliments a basic on disk caching. That will work fine on a single host
  * but on a multi server setup this could lead to some problems due to inconsistencies
  * between the various cached versions on the different servers. Other methods like
  * memcached should be used instead really.
- * 
+ *
  * When using this file based backend, its adviced to make a cron job that scans thru the
  * cache dir, and removes all files that are older then 24 hours (or whatever your
  * config's CACHE_TIME is set too).
@@ -55,14 +55,13 @@
   }
 
   private function waitForLock($cacheFile) {
-    // 20 x 250 = 5 seconds
-    $tries = 20;
+    // 10 x 100 = 1 second
+    $tries = 10;
     $cnt = 0;
     do {
       // make sure PHP picks up on file changes. This is an expensive action but really can't be avoided
       clearstatcache();
-      // 250 ms is a long time to sleep, but it does stop the server from burning all resources on polling locks..
-      usleep(250);
+      usleep(100);
       $cnt ++;
     } while ($cnt <= $tries && $this->isLocked($cacheFile));
     if ($this->isLocked($cacheFile)) {
@@ -82,11 +81,7 @@
     return $this->getCacheDir($hash) . '/' . $hash;
   }
 
-  public function get($key, $expiration = false) {
-    if (! $expiration) {
-      // if no expiration time was given, fall back on the global config
-      $expiration = Config::get('cache_time');
-    }
+  public function get($key) {
     $cacheFile = $this->getCacheFile($key);
     // See if this cache file is locked, if so we wait upto 5 seconds for the lock owning process to
     // complete it's work. If the lock is not released within that time frame, it's cleaned up.
@@ -95,18 +90,20 @@
       $this->waitForLock($cacheFile);
     }
     if (File::exists($cacheFile) && File::readable($cacheFile)) {
-      $now = time();
-      if (($mtime = @filemtime($cacheFile)) !== false && ($now - $mtime) < $expiration) {
-        if (($data = @file_get_contents($cacheFile)) !== false) {
-          $data = unserialize($data);
-          return $data;
+      if (($data = @file_get_contents($cacheFile)) !== false) {
+        $data = unserialize($data);
+        if (($_SERVER['REQUEST_TIME'] - $data['time']) < $data['ttl']) {
+          return $data['data'];
         }
       }
     }
     return false;
   }
 
-  public function set($key, $value) {
+  public function set($key, $value, $ttl = false) {
+    if (! $ttl) {
+      $ttl = Config::Get('cache_time');
+    }
     $cacheDir = $this->getCacheDir($key);
     $cacheFile = $this->getCacheFile($key);
     if ($this->isLocked($cacheFile)) {
@@ -118,9 +115,7 @@
         throw new CacheException("Could not create cache directory");
       }
     }
-    // we serialize the whole request object, since we don't only want the
-    // responseContent but also the postBody used, headers, size, etc
-    $data = serialize($value);
+    $data = serialize(array('data' => $value, 'time' => $_SERVER['REQUEST_TIME'], 'ttl' => $ttl));
     $this->createLock($cacheFile);
     if (! @file_put_contents($cacheFile, $data)) {
       $this->removeLock($cacheFile);

Modified: incubator/shindig/trunk/php/src/common/sample/CacheMemcache.php
URL: http://svn.apache.org/viewvc/incubator/shindig/trunk/php/src/common/sample/CacheMemcache.php?rev=744970&r1=744969&r2=744970&view=diff
==============================================================================
--- incubator/shindig/trunk/php/src/common/sample/CacheMemcache.php (original)
+++ incubator/shindig/trunk/php/src/common/sample/CacheMemcache.php Tue Feb 17 11:53:06 2009
@@ -52,7 +52,7 @@
     $this->check();
     // the interesting thing is that this could fail if the lock was created in the meantime..
     // but we'll ignore that out of convenience
-    @memcache_add($this->connection, $key . '.lock', '', 0, 5);
+    @memcache_add($this->connection, $key . '.lock', '', 0, 2);
   }
 
   private function removeLock($key) {
@@ -63,22 +63,18 @@
 
   private function waitForLock($key) {
     $this->check();
-    // 20 x 250 = 5 seconds
-    $tries = 20;
+    $tries = 10;
     $cnt = 0;
     do {
-      // 250 ms is a long time to sleep, but it does stop the server from burning all resources on polling locks..
-      usleep(250);
+      usleep(100);
       $cnt ++;
     } while ($cnt <= $tries && $this->isLocked());
     if ($this->isLocked()) {
-      // 5 seconds passed, assume the owning process died off and remove it
       $this->removeLock($key);
     }
   }
 
-  // I prefer lazy initalization since the cache isn't used every request
-  // so this potentially saves a lot of overhead
+  // Prefer lazy initalization since the cache isn't used every request
   private function connect() {
     if (! $this->connection = @memcache_pconnect($this->host, $this->port)) {
       throw new CacheException("Couldn't connect to memcache server");
@@ -107,13 +103,20 @@
     return $ret['data'];
   }
 
-  public function set($key, $value) {
+  public function set($key, $value, $ttl = false) {
     $this->check();
-    // we store it with the cache_time default expiration so objects will atleast get cleaned eventually.
-    if (@memcache_set($this->connection, $key, array('time' => time(),
-        'data' => $value), false, Config::Get('cache_time')) == false) {
+    if (! $ttl) {
+      $ttl = Config::Get('cache_time');
+    }
+    if ($this->isLocked($key)) {
+      $this->waitForLock($key);
+    }
+    $this->createLock($key);
+    if (@memcache_set($this->connection, $key, $value, false, $ttl) == false) {
+      $this->removeLock($key);
       throw new CacheException("Couldn't store data in cache");
     }
+    $this->removeLock($key);
   }
 
   public function delete($key) {

Modified: incubator/shindig/trunk/php/src/gadgets/SigningFetcher.php
URL: http://svn.apache.org/viewvc/incubator/shindig/trunk/php/src/gadgets/SigningFetcher.php?rev=744970&r1=744969&r2=744970&view=diff
==============================================================================
--- incubator/shindig/trunk/php/src/gadgets/SigningFetcher.php (original)
+++ incubator/shindig/trunk/php/src/gadgets/SigningFetcher.php Tue Feb 17 11:53:06 2009
@@ -28,25 +28,25 @@
  * but instances may be created by multiple threads.
  */
 class SigningFetcher extends RemoteContentFetcher {
-  
+
   protected static $OPENSOCIAL_OWNERID = "opensocial_owner_id";
   protected static $OPENSOCIAL_VIEWERID = "opensocial_viewer_id";
   protected static $OPENSOCIAL_APPID = "opensocial_app_id";
   protected static $XOAUTH_PUBLIC_KEY = "xoauth_signature_publickey";
   protected static $ALLOWED_PARAM_NAME = '^[-_[:alnum:]]+$';
-  
+
   /**
    * Authentication token for the user and gadget making the request.
    */
   protected $authToken;
-  
+
   /**
    * Private key we pass to the OAuth RSA_SHA1 algorithm.This can be a
    * PrivateKey object, or a PEM formatted private key, or a DER encoded byte
    * array for the private key.(No, really, they accept any of them.)
    */
   protected $privateKeyObject;
-  
+
   /**
    * The name of the key, included in the fetch to help with key rotation.
    */
@@ -92,7 +92,7 @@
     $this->privateKeyObject = $privateKeyObject;
   }
 
-  public function fetchRequest($request) {
+  public function fetchRequest(RemoteContentRequest $request) {
     return $this->getNextFetcher()->fetchRequest($request);
   }