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 2008/10/09 22:58:34 UTC

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

Author: chabotc
Date: Thu Oct  9 13:58:33 2008
New Revision: 703257

URL: http://svn.apache.org/viewvc?rev=703257&view=rev
Log:
SHINDIG-542 by Bruno rovagnati, implement curl_multi fetcher for fetching multiple http requests simultanious, plus use that in the preloading code (and hopefully soon for the new message bundle handling too thats still pending)

Modified:
    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/gadgets/SigningFetcher.php
    incubator/shindig/trunk/php/src/gadgets/servlet/GadgetRenderingServlet.php

Modified: incubator/shindig/trunk/php/src/common/RemoteContent.php
URL: http://svn.apache.org/viewvc/incubator/shindig/trunk/php/src/common/RemoteContent.php?rev=703257&r1=703256&r2=703257&view=diff
==============================================================================
--- incubator/shindig/trunk/php/src/common/RemoteContent.php (original)
+++ incubator/shindig/trunk/php/src/common/RemoteContent.php Thu Oct  9 13:58:33 2008
@@ -31,4 +31,6 @@
 abstract class RemoteContent {
 
 	abstract public function fetch($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=703257&r1=703256&r2=703257&view=diff
==============================================================================
--- incubator/shindig/trunk/php/src/common/RemoteContentFetcher.php (original)
+++ incubator/shindig/trunk/php/src/common/RemoteContentFetcher.php Thu Oct  9 13:58:33 2008
@@ -29,6 +29,8 @@
 
 	abstract public function fetchRequest($request);
 
+	abstract public function multiFetchRequest(Array $requests);
+
 	public function getNextFetcher()
 	{
 		return $this->fetcher;

Modified: incubator/shindig/trunk/php/src/common/RemoteContentRequest.php
URL: http://svn.apache.org/viewvc/incubator/shindig/trunk/php/src/common/RemoteContentRequest.php?rev=703257&r1=703256&r2=703257&view=diff
==============================================================================
--- incubator/shindig/trunk/php/src/common/RemoteContentRequest.php (original)
+++ incubator/shindig/trunk/php/src/common/RemoteContentRequest.php Thu Oct  9 13:58:33 2008
@@ -21,6 +21,8 @@
 class RemoteContentRequest {
 	// these are used for making the request
 	private $uri = '';
+	// to get real url after signed requests
+	private $notSignedUri = '';
 	private $method = '';
 	private $headers = array();
 	private $postBody = false;
@@ -39,6 +41,7 @@
 	public function __construct($uri, $headers = false, $postBody = false)
 	{
 		$this->uri = $uri;
+		$this->notSignedUri = $uri;
 		$this->headers = $headers;
 		$this->postBody = $postBody;
 		$this->created = time();
@@ -279,6 +282,11 @@
 		return $this->uri;
 	}
 
+	public function getNotSignedUrl()
+	{
+		return $this->notSignedUri;
+	}
+
 	public function getMethod()
 	{
 		return $this->method;
@@ -363,6 +371,11 @@
 	{
 		$this->uri = $uri;
 	}
+
+	public function setNotSignedUri($uri)
+	{
+		$this->notSignedUri = $uri;
+	}
 }
 
 /**

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=703257&r1=703256&r2=703257&view=diff
==============================================================================
--- incubator/shindig/trunk/php/src/common/sample/BasicRemoteContent.php (original)
+++ incubator/shindig/trunk/php/src/common/sample/BasicRemoteContent.php Thu Oct  9 13:58:33 2008
@@ -33,11 +33,46 @@
 			$ret = $cachedRequest;
 		} else {
 			$ret = $remoteContentFetcher->fetchRequest($request);
-			// only cache requests that returned a 200 OK
-			if ($request->getHttpCode() == '200') {
+			// only cache requests that returned a 200 OK and is not a POST
+			if ($request->getHttpCode() == '200' && ! $request->isPost()) {
 				$cache->set($request->toHash(), $request);
 			}
 		}
 		return $ret;
 	}
+
+	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) {
+				$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);
+			}
+			$rets[] = $request;
+		}
+		
+		return $rets;
+	}
 }
\ No newline at end of file

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=703257&r1=703256&r2=703257&view=diff
==============================================================================
--- incubator/shindig/trunk/php/src/common/sample/BasicRemoteContentFetcher.php (original)
+++ incubator/shindig/trunk/php/src/common/sample/BasicRemoteContentFetcher.php Thu Oct  9 13:58:33 2008
@@ -82,4 +82,91 @@
 		unset($request->handle);
 		return $request;
 	}
+
+	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, 2);
+			curl_setopt($request->handle, CURLOPT_TIMEOUT, 2);
+			curl_setopt($request->handle, CURLOPT_HEADER, 1);
+			curl_setopt($request->handle, CURLOPT_SSL_VERIFYPEER, 0);
+			// 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 (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: Shindig PHP";
+				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());
+			}
+			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);
+			unset($request->handle);
+		}
+		curl_multi_close($mh);
+		unset($mh);
+		
+		return $requests;
+	}
 }

Modified: incubator/shindig/trunk/php/src/gadgets/SigningFetcher.php
URL: http://svn.apache.org/viewvc/incubator/shindig/trunk/php/src/gadgets/SigningFetcher.php?rev=703257&r1=703256&r2=703257&view=diff
==============================================================================
--- incubator/shindig/trunk/php/src/gadgets/SigningFetcher.php (original)
+++ incubator/shindig/trunk/php/src/gadgets/SigningFetcher.php Thu Oct  9 13:58:33 2008
@@ -102,7 +102,12 @@
 		return $this->getNextFetcher()->fetchRequest($signed);
 	}
 
-	private function signRequest($url, $method)
+	public function multiFetchRequest(Array $requests)
+	{
+		return $this->getNextFetcher()->multiFetchRequest($requests);
+	}
+
+	public function signRequest($url, $method)
 	{
 		try {
 			// Parse the request into parameters for OAuth signing, stripping out
@@ -238,5 +243,4 @@
 		// make a last sanity check on the key of the data by using a regular expression
 		return ereg(SigningFetcher::$ALLOWED_PARAM_NAME, $canonParamName);
 	}
-
 }

Modified: incubator/shindig/trunk/php/src/gadgets/servlet/GadgetRenderingServlet.php
URL: http://svn.apache.org/viewvc/incubator/shindig/trunk/php/src/gadgets/servlet/GadgetRenderingServlet.php?rev=703257&r1=703256&r2=703257&view=diff
==============================================================================
--- incubator/shindig/trunk/php/src/gadgets/servlet/GadgetRenderingServlet.php (original)
+++ incubator/shindig/trunk/php/src/gadgets/servlet/GadgetRenderingServlet.php Thu Oct  9 13:58:33 2008
@@ -361,7 +361,7 @@
 	 */
 	private function appendPreloads(Gadget $gadget, GadgetContext $context)
 	{
-		$resp = '';
+		$resp = Array();
 		$gadgetSigner = Config::get('security_token_signer');
 		$gadgetSigner = new $gadgetSigner();
 		$token = '';
@@ -371,6 +371,8 @@
 			$token = '';
 			// no token given, safe to ignore
 		}
+		$unsignedRequests = $unsignedContexts = Array();
+		$signedRequests = Array();
 		foreach ($gadget->getPreloads() as $preload) {
 			try {
 				if (($preload->getAuth() == Auth::$NONE || $token != null) && (count($preload->getViews()) == 0 || in_array($context->getView(), $preload->getViews()))) {
@@ -380,13 +382,17 @@
 					$request->getOptions()->viewerSigned = $preload->isSignViewer();
 					switch (strtoupper(trim($preload->getAuth()))) {
 						case "NONE":
-							$brc = new BasicRemoteContent();
-							$response = $brc->fetch($request, $context);
+							//						Unify all unsigned requests to one single multi request
+							$unsignedRequests[] = $request;
+							$unsignedContexts[] = $context;
 							break;
 						case "SIGNED":
+							//						Unify all signed requests to one single multi request
 							$signingFetcherFactory = new SigningFetcherFactory(Config::get("private_key_file"));
 							$fetcher = $signingFetcherFactory->getSigningFetcher(new BasicRemoteContentFetcher(), $token);
-							$response = $fetcher->fetch($preload->getHref(), $request->getMethod());
+							$req = $fetcher->signRequest($preload->getHref(), $request->getMethod());
+							$req->setNotSignedUri($preload->getHref());
+							$signedRequests[] = $req;
 							break;
 						default:
 							@ob_end_clean();
@@ -394,7 +400,17 @@
 							echo "<html><body><h1>" . "500 - Internal Server Error" . "</h1></body></html>";
 							die();
 					}
-					$resp[$preload->getHref()] = array(
+				}
+			} catch (Exception $e) {
+				throw new Exception($e);
+			}
+		}
+		if (count($unsignedRequests)) {
+			try {
+				$brc = new BasicRemoteContent();
+				$responses = $brc->multiFetch($unsignedRequests, $unsignedContexts);
+				foreach ($responses as $response) {
+					$resp[$response->getUrl()] = array(
 							'body' => $response->getResponseContent(), 
 							'rc' => $response->getHttpCode());
 				}
@@ -402,8 +418,20 @@
 				throw new Exception($e);
 			}
 		}
+		if (count($signedRequests)) {
+			try {
+				$fetcher = $signingFetcherFactory->getSigningFetcher(new BasicRemoteContentFetcher(), $token);
+				$responses = $fetcher->multiFetchRequest($signedRequests);
+				foreach ($responses as $response) {
+					$resp[$response->getNotSignedUrl()] = array(
+							'body' => $response->getResponseContent(), 
+							'rc' => $response->getHttpCode());
+				}
+			} catch (Exception $e) {
+				throw new Exception($e);
+			}
+		}		
 		$resp = count($resp) ? json_encode($resp) : "{}";
 		return "gadgets.io.preloaded_ = " . $resp . ";\n";
 	}
-
-}
\ No newline at end of file
+}