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/04/01 01:12:00 UTC
svn commit: r760722 - in /incubator/shindig/trunk/php/src:
common/RemoteContentRequest.php common/sample/BasicRemoteContent.php
gadgets/GadgetSpecParser.php gadgets/render/GadgetHrefRenderer.php
gadgets/sample/BasicGadgetSpecFactory.php
Author: chabotc
Date: Tue Mar 31 23:12:00 2009
New Revision: 760722
URL: http://svn.apache.org/viewvc?rev=760722&view=rev
Log:
This adds a mostly working data-pipelining implementation, the remaining issues that need to be solved before it's complete are:
1) The signing fetcher currently uses the $_POST superglobal and not the $request->getPostBody() to build the signature, resulting in invalid oauth sig's
2) the signing fetch also uses the same, parsed, $_POST vars, to set a new post body on the request, removing the actual post body with the data-pipelining information
3) JSP-EL type parsing of dynamic tags still needs to be implemented.
Almost there though! :)
Modified:
incubator/shindig/trunk/php/src/common/RemoteContentRequest.php
incubator/shindig/trunk/php/src/common/sample/BasicRemoteContent.php
incubator/shindig/trunk/php/src/gadgets/GadgetSpecParser.php
incubator/shindig/trunk/php/src/gadgets/render/GadgetHrefRenderer.php
incubator/shindig/trunk/php/src/gadgets/sample/BasicGadgetSpecFactory.php
Modified: incubator/shindig/trunk/php/src/common/RemoteContentRequest.php
URL: http://svn.apache.org/viewvc/incubator/shindig/trunk/php/src/common/RemoteContentRequest.php?rev=760722&r1=760721&r2=760722&view=diff
==============================================================================
--- incubator/shindig/trunk/php/src/common/RemoteContentRequest.php (original)
+++ incubator/shindig/trunk/php/src/common/RemoteContentRequest.php Tue Mar 31 23:12:00 2009
@@ -42,7 +42,7 @@
* @var Options
*/
private $options;
-
+
/**
* @var SecurityToken
*/
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=760722&r1=760721&r2=760722&view=diff
==============================================================================
--- incubator/shindig/trunk/php/src/common/sample/BasicRemoteContent.php (original)
+++ incubator/shindig/trunk/php/src/common/sample/BasicRemoteContent.php Tue Mar 31 23:12:00 2009
@@ -158,7 +158,7 @@
$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
+ // See http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html : The Cache-Control: max-age=<seconds> overrides the expires header, so 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) {
Modified: incubator/shindig/trunk/php/src/gadgets/GadgetSpecParser.php
URL: http://svn.apache.org/viewvc/incubator/shindig/trunk/php/src/gadgets/GadgetSpecParser.php?rev=760722&r1=760721&r2=760722&view=diff
==============================================================================
--- incubator/shindig/trunk/php/src/gadgets/GadgetSpecParser.php (original)
+++ incubator/shindig/trunk/php/src/gadgets/GadgetSpecParser.php Tue Mar 31 23:12:00 2009
@@ -129,7 +129,7 @@
break;
case 'os:ActivitiesRequest':
$tag['type'] = 'os:DataRequest';
- $tag['method'] = 'activity.get';
+ $tag['method'] = 'activities.get';
break;
}
$dataPipeliningTags[] = $tag;
Modified: incubator/shindig/trunk/php/src/gadgets/render/GadgetHrefRenderer.php
URL: http://svn.apache.org/viewvc/incubator/shindig/trunk/php/src/gadgets/render/GadgetHrefRenderer.php?rev=760722&r1=760721&r2=760722&view=diff
==============================================================================
--- incubator/shindig/trunk/php/src/gadgets/render/GadgetHrefRenderer.php (original)
+++ incubator/shindig/trunk/php/src/gadgets/render/GadgetHrefRenderer.php Tue Mar 31 23:12:00 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,6 +19,27 @@
* under the License.
*/
+/*
+ * TODO Dynamically evaluate the limited EL subset expressions on the following tags:
+ * Any attribute on os:DataRequest other than @key and @method
+ * @userId
+ * @groupId
+ * @fields
+ * @startIndex
+ * @count
+ * @sortBy
+ * @sortOrder
+ * @filterBy
+ * @filterOp
+ * @filterValue
+ * @activityIds
+ * @href
+ * @params
+ * Example:
+ * <os:PeopleRequest key="PagedFriends" userId="@owner" groupId="@friends" startIndex="${ViewParams.first}" count="20"/>
+ * <os:HttpRequest href="http://developersite.com/api?ids=${PagedFriends.ids}"/>
+ */
+
class GadgetHrefRenderer extends GadgetRenderer {
/**
@@ -28,6 +50,12 @@
* @param array $view
*/
public function renderGadget(Gadget $gadget, $view) {
+
+ $dataPipelining = false;
+ if (count($view['dataPipelining'])) {
+ $dataPipelining = $this->fetchDataPipelining($view['dataPipelining']);
+ }
+
/* TODO
* We should really re-add OAuth fetching support some day, uses these view atributes:
* $view['oauthServiceName'], $view['oauthTokenName'], $view['oauthRequestToken'], $view['oauthRequestTokenSecret'];
@@ -42,28 +70,162 @@
// rewrite our $_GET to match the outgoing request, this is currently needed for the oauth library
// to generate it's correct signature
- $_GET = $_POST = array();
$uri = parse_url($href);
parse_str($uri['query'], $_GET);
- $request = new RemoteContentRequest($href);
- $request->setMethod('GET');
- $request->setToken($token);
- $request->setRefreshInterval($refreshInterval);
- $request->setAuthType($authz);
+ if ($dataPipelining) {
+ // if data-pipeling results are set in $dataPipelining, post the json encoded version to the remote url
+ $request = new RemoteContentRequest($href, "Content-type: application/json\n", json_encode($dataPipelining));
+ $request->setMethod('POST');
+ } else {
+ // no data-pipelining set, use GET and set cache/refresh interval options
+ $request = new RemoteContentRequest($href);
+ $request->setMethod('GET');
+ $request->setRefreshInterval($refreshInterval);
+ $request->getOptions()->ignoreCache = $gadget->gadgetContext->getIgnoreCache();
+ }
- $signingFetcherFactory = false;
+ $signingFetcherFactory = $gadgetSigner = false;
if ($authz != 'none') {
+ $request->setToken($token);
+ $request->setAuthType($authz);
$signingFetcherFactory = new SigningFetcherFactory(Config::get("private_key_file"));
+ $_GET = $_POST = array();
}
$basicFetcher = new BasicRemoteContentFetcher();
$basicRemoteContent = new BasicRemoteContent($basicFetcher, $signingFetcherFactory, $gadgetSigner);
- $response = $basicRemoteContent->fetch($request, $gadget->gadgetContext, $authz);
+ $response = $basicRemoteContent->fetch($request);
echo $response->getResponseContent();
}
/**
+ * Fetches the requested data-pipeling info
+ *
+ * @param array $dataPipelining contains the parsed data-pipelining tags
+ * @return array result
+ */
+ private function fetchDataPipelining($dataPipelining) {
+ $result = array();
+ do {
+ // See which requests we can batch together, that either don't use dynamic tags or who's tags are resolvable
+ $requestQueue = array();
+ foreach ($dataPipelining as $key => $request) {
+ if (($resolved = $this->resolveRequest($request, $result)) !== false) {
+ $requestQueue[] = $resolved;
+ unset($dataPipelining[$key]);
+ }
+ }
+ if (count($requestQueue)) {
+ $result = array_merge($this->performRequests($requestQueue), $result);
+ }
+ } while (count($requestQueue));
+ return $result;
+ }
+
+ /**
+ * Peforms the actual http fetching of the data-pipelining requests, all social requests
+ * are made to $_SERVER['SERVER_NAME'] (the virtual host name of this server) / (optional) web_prefix / social / rpc, and
+ * the httpRequest's are made to $_SERVER['SERVER_NAME'] (the virtual host name of this server) / (optional) web_prefix / gadgets / makeRequest
+ * both request types use the current security token ($_GET['st']) when performing the requests so they happen in the correct context
+ *
+ * @param array $requests
+ * @return array response
+ */
+ private function performRequests($requests) {
+ $jsonRequests = array();
+ $httpRequests = array();
+ $decodedResponse = array();
+ // Using the same gadget security token for all social & http requests so everything happens in the right context
+ $securityToken = $_GET['st'];
+ foreach ($requests as $request) {
+ switch ($request['type']) {
+ case 'os:DataRequest':
+ // Add to the social request batch
+ $id = $request['key'];
+ $method = $request['method'];
+ // remove our internal fields so we can use the remainder as params
+ unset($request['key']);
+ unset($request['method']);
+ unset($request['type']);
+ $jsonRequests[] = array('method' => $method, 'id' => $id, 'params' => $request);
+ break;
+ case 'os:HttpRequest':
+ $id = $request['key'];
+ $url = $request['href'];
+ unset($request['key']);
+ unset($request['type']);
+ unset($request['href']);
+ $httpRequests[] = array('id' => $id, 'url' => $url, 'queryStr' => implode('&', $request));
+ break;
+ }
+ }
+ if (count($jsonRequests)) {
+ // perform social api requests
+ $request = new RemoteContentRequest($_SERVER['SERVER_NAME'] . Config::get('web_prefix') . '/social/rpc?st=' . urlencode($securityToken) . '&format=json', "Content-type: application/json\n", json_encode($jsonRequests));
+ $request->setMethod('POST');
+ $basicFetcher = new BasicRemoteContentFetcher();
+ $basicRemoteContent = new BasicRemoteContent($basicFetcher);
+ $response = $basicRemoteContent->fetch($request);
+ $decodedResponse = json_decode($response->getResponseContent(), true);
+ }
+ if (count($httpRequests)) {
+ $requestQueue = array();
+ foreach ($httpRequests as $request) {
+ $req = new RemoteContentRequest($_SERVER['SERVER_NAME'] . Config::get('web_prefix') . '/gadgets/makeRequest?url=' . urlencode($request['url']) . '&st=' . urlencode($securityToken) . (! empty($request['queryStr']) ? '&' . $request['queryStr'] : ''));
+ $req->getOptions()->ignoreCache = $this->context->getIgnoreCache();
+ $req->setNotSignedUri($request['url']);
+ $requestQueue[] = $req;
+ }
+ $basicRemoteContent = new BasicRemoteContent();
+ $resps = $basicRemoteContent->multiFetch($requestQueue);
+ foreach ($resps as $response) {
+ // strip out the UNPARSEABLE_CRUFT (see makeRequestHandler.php) on assigning the body
+ $resp = json_decode(str_replace("throw 1; < don't be evil' >", '', $response->getResponseContent()), true);
+ if (is_array($resp)) {
+ //FIXME: make sure that this is the format that java-shindig produces as well, the spec doesn't really state
+ $decodedResponse = array_merge($resp, $decodedResponse);
+ }
+ }
+ }
+ return $decodedResponse;
+ }
+
+ /**
+ * If a request (data-pipelining tag) doesn't include any dynamic tags, it's returned as is. If
+ * however it does contain said tag, this function will attempt to resolve it using the $result
+ * array, returning the parsed request on success, or FALSE on failure to resolve.
+ *
+ * @param array $request
+ */
+ private function resolveRequest($request, $result) {
+ foreach ($request as $key => $val) {
+ if (($pos = strpos($val, '${')) !== false) {
+ $key = substr($val, $pos + 2);
+ $key = substr($key, 0, strpos($key, '}'));
+ if (($resolved = $this->resolveExpression($key, $result)) !== null) {
+ $request[$key] = str_replace('${' . $key . '}', $resolved, $val);
+ } else {
+ return false;
+ }
+ }
+ }
+ return $request;
+ }
+
+ /**
+ * Resolves simplified JSP-EL expressions if the matching entry exists in $data
+ *
+ * @param string $expression
+ * @param array $data
+ */
+ private function resolveExpression($expression, $data) {
+ //TODO implement this, see http://opensocial-resources.googlecode.com/svn/spec/draft/OpenSocial-Data-Pipelining.xml#rfc.section.14
+ // always return null (aka can't resolve) until it's implemented
+ return null;
+ }
+
+ /**
* Builds the outgoing URL by taking the href attribute of the view and appending
* the country, lang, and opensocial query params to it
*
@@ -75,7 +237,7 @@
$href = $view['href'];
if (empty($href)) {
throw new Exception("Invalid empty href in the gadget view");
- } // add the required country and lang param to the URL
+ } // add the required country and lang param to the URL
$lang = isset($_GET['lang']) ? $_GET['lang'] : 'en';
$country = isset($_GET['country']) ? $_GET['country'] : 'US';
$firstSeperator = strpos($href, '?') === false ? '?' : '&';
Modified: incubator/shindig/trunk/php/src/gadgets/sample/BasicGadgetSpecFactory.php
URL: http://svn.apache.org/viewvc/incubator/shindig/trunk/php/src/gadgets/sample/BasicGadgetSpecFactory.php?rev=760722&r1=760721&r2=760722&view=diff
==============================================================================
--- incubator/shindig/trunk/php/src/gadgets/sample/BasicGadgetSpecFactory.php (original)
+++ incubator/shindig/trunk/php/src/gadgets/sample/BasicGadgetSpecFactory.php Tue Mar 31 23:12:00 2009
@@ -47,7 +47,10 @@
*/
private function fetchFromWeb($url, $ignoreCache) {
$remoteContentRequest = new RemoteContentRequest($url);
- $remoteContentRequest->getRequest($url, $ignoreCache);
+ $remoteContentRequest->getOptions()->ignoreCache = $ignoreCache;
+ $remoteContent = new BasicRemoteContent($this->fetcher);
+ $spec = $remoteContent->fetch($remoteContentRequest);
+
$spec = $this->fetcher->fetchRequest($remoteContentRequest);
$specParser = new GadgetSpecParser();
$context = new ProxyGadgetContext($url);