hmmm, if you send a file though a simple <form> isnt it multiparted by the
browser?

ropu

On Fri, Jul 18, 2008 at 8:04 PM, Chris Chabot <[EMAIL PROTECTED]> wrote:

> This has been a bit of a headache to develop (see spec list) but it's
> starting to take some shape. It's also quite difficult to test for me, since
> nothing in PHP really supports multipart http posts... So i had to hand
> craft both a test client, the input parsing in shindig, and the multi part
> output construct too.
>
> In other words, it works with the input that my test client creates, but i
> have no way to absolutely verify it is all completely valid http protocol
> compliant stuff ... ah the joys of developing in the dark with blindfolds on
> :)
>
> The batch proxy is advertised through XRDS simple:
> http://www.chabotc.com/xrds-test.php?url=http://www.partuza.nl
>
> When i shoot a hand crafted multipart payload into it it works & you can
> specify how you want your (presumably http multipart compliant) output on
> the main url (to batchProxy) with ?format=atom or ?format=json (json is
> assumed by default). You could even poke at it at
> http://modules.partuza.nl/social/rest/batchProxy .. just make sure to grab
> a valid token from a gadget iframe first to use in your requests and your
> golden :) The only missing bits are OAuth support, and Atom format input
> support, they will hopefully follow shortly.
>
> However my main question ... does anyone have any useful tools for actually
> testing and verifying multipart http posts & the multipart response ? :)
>
>        -- Chris
>
> Begin forwarded message:
>
>  From: [EMAIL PROTECTED]
>> Date: July 19, 2008 12:47:41 AM GMT+02:00
>> To: [EMAIL PROTECTED]
>> Subject: svn commit: r678068 - in
>> /incubator/shindig/trunk/php/src/social-api: converters/ dataservice/ http/
>> samplecontainer/
>> Reply-To: [email protected]
>>
>> Author: chabotc
>> Date: Fri Jul 18 15:47:41 2008
>> New Revision: 678068
>>
>> URL: http://svn.apache.org/viewvc?rev=678068&view=rev
>> Log:
>> Initial support for the batch proxy request type.
>>
>> The url is /social/rest/batchProxy and it uses the http multipart
>> format as described in the RESTful API specification.
>>
>> It only supports ONE output format for a set of requests (which
>> is determined by the main url ?format=foo param, and defaults to
>> json). Input however will support mixing json and atom.
>>
>> Atom input is still missing (has a debug dump right now) but support
>> for that will follow quickly.
>>
>> OAuth and Atom input is still missing, but once their done PHP
>> Shindig will have full RESTful spec support, we're getting there!
>>
>>
>> Modified:
>>
>> incubator/shindig/trunk/php/src/social-api/converters/OutputAtomConverter.php
>>
>> incubator/shindig/trunk/php/src/social-api/converters/OutputConverter.php
>>
>> incubator/shindig/trunk/php/src/social-api/converters/OutputJsonConverter.php
>>   incubator/shindig/trunk/php/src/social-api/dataservice/PeopleHandler.php
>>
>> incubator/shindig/trunk/php/src/social-api/dataservice/RestRequestItem.php
>>   incubator/shindig/trunk/php/src/social-api/http/RestServlet.php
>>
>> incubator/shindig/trunk/php/src/social-api/samplecontainer/BasicAppDataService.php
>>
>> incubator/shindig/trunk/php/src/social-api/samplecontainer/BasicPeopleService.php
>>
>> Modified:
>> incubator/shindig/trunk/php/src/social-api/converters/OutputAtomConverter.php
>> URL:
>> http://svn.apache.org/viewvc/incubator/shindig/trunk/php/src/social-api/converters/OutputAtomConverter.php?rev=678068&r1=678067&r2=678068&view=diff
>>
>> ==============================================================================
>> ---
>> incubator/shindig/trunk/php/src/social-api/converters/OutputAtomConverter.php
>> (original)
>> +++
>> incubator/shindig/trunk/php/src/social-api/converters/OutputAtomConverter.php
>> Fri Jul 18 15:47:41 2008
>> @@ -1,4 +1,5 @@
>> <?php
>> +
>> /*
>>  * Licensed to the Apache Software Foundation (ASF) under one
>>  * or more contributor license agreements. See the NOTICE file
>> @@ -19,7 +20,7 @@
>>
>> /**
>>  * Format = atom output converter, for format definition see:
>> - *
>> http://www.opensocial.org/Technical-Resources/opensocial-specification----implementation-version-08/restful-api-specification
>> + *
>> http://sites.google.com/a/opensocial.org/opensocial/Technical-Resources/opensocial-spec-v08/restful-api-specification
>>  */
>> class OutputAtomConverter extends OutputConverter {
>>        private static $nameSpace = 'http://www.w3.org/2005/Atom';
>> @@ -40,7 +41,7 @@
>>                $data = $responseItem->getResponse();
>>                $userId =
>> $requestItem->getUser()->getUserId($requestItem->getToken());
>>                $guid = 'urn:guid:' . $userId;
>> -               $authorName = $_SERVER['HTTP_HOST'].':'.$userId;
>> +               $authorName = $_SERVER['HTTP_HOST'] . ':' . $userId;
>>                $updatedAtom = date(DATE_ATOM);
>>
>>                // Check to see if this is a single entry, or a collection,
>> and construct either an atom
>> @@ -52,20 +53,18 @@
>>
>>                        // The root Feed element
>>                        $entry = $this->addNode($doc, 'feed', '', false,
>> self::$nameSpace);
>> -
>> +
>>                        // Required Atom fields
>>                        $endPos = ($startIndex + $itemsPerPage) >
>> $totalResults ? $totalResults : ($startIndex + $itemsPerPage);
>> -                       $this->addNode($entry, 'title', $requestType.'
>> feed for id '.$authorName.' ('.$startIndex. ' - '. ($endPos - 1).' of
>> '.$totalResults.')');
>> +                       $this->addNode($entry, 'title', $requestType . '
>> feed for id ' . $authorName . ' (' . $startIndex . ' - ' . ($endPos - 1) . '
>> of ' . $totalResults . ')');
>>                        $author = $this->addNode($entry, 'author');
>>                        $this->addNode($author, 'uri', $guid);
>> -                       $this->addNode($author, 'name', $authorName);
>>
>> +                       $this->addNode($author, 'name', $authorName);
>>                        $this->addNode($entry, 'updated', $updatedAtom);
>>                        $this->addNode($entry, 'id', $guid);
>> -                       $this->addNode($entry, 'link', '', array('rel' =>
>> 'self', 'href' => 'http://
>> '.$_SERVER['HTTP_HOST'].$_SERVER['REQUEST_URI']));
>> -
>> +                       $this->addNode($entry, 'link', '', array('rel' =>
>> 'self', 'href' => 'http://' . $_SERVER['HTTP_HOST'] .
>> $_SERVER['REQUEST_URI']));
>>                        // Add osearch & next link to the entry
>>                        $this->addPagingFields($entry, $startIndex,
>> $itemsPerPage, $totalResults);
>> -
>>                        // Add response entries to feed
>>                        $responses =
>> $responseItem->getResponse()->getEntry();
>>                        foreach ($responses as $response) {
>> @@ -79,11 +78,10 @@
>>                                $this->addNode($author, 'uri', $guid);
>>                                $this->addNode($author, 'name',
>> $authorName);
>>                                // Special hoisting rules for activities
>> -
>>                                if ($response instanceof Activity) {
>>                                        $this->addNode($feedEntry,
>> 'category', '', array('term' => 'status'));
>>                                        $this->addNode($feedEntry,
>> 'updated', date(DATE_ATOM, $response->postedTime));
>> -                                       $this->addNode($feedEntry, 'id',
>> 'urn:guid:'.$response->id);
>> +                                       $this->addNode($feedEntry, 'id',
>> 'urn:guid:' . $response->id);
>>                                        //FIXME should add a link field but
>> don't have URL's available yet:
>>                                        // <link rel="self"
>> type="application/atom+xml" href="
>> http://api.example.org/activity/feeds/.../af3778"/>
>>                                        $this->addNode($feedEntry, 'title',
>> strip_tags($response->title));
>> @@ -94,43 +92,49 @@
>>                                        unset($response->title);
>>                                        unset($response->body);
>>                                } else {
>> -                                       $this->addNode($feedEntry, 'id',
>> 'urn:guid:'.$idField);
>> -                                       $this->addNode($feedEntry,
>> 'title', $requestType.' feed entry for id '.$idField);
>> +                                       $this->addNode($feedEntry, 'id',
>> 'urn:guid:' . $idField);
>> +                                       $this->addNode($feedEntry,
>> 'title', $requestType . ' feed entry for id ' . $idField);
>>                                        $this->addNode($feedEntry,
>> 'updated', $updatedAtom);
>>                                }
>>
>>                                // recursively add responseItem data to the
>> xml structure
>>                                $this->addData($content, $requestType,
>> $response, self::$osNameSpace);
>>                        }
>> -
>>                } else {
>> -
>>                        // Single entry = Atom:Entry
>>                        $entry =
>> $doc->appendChild($doc->createElementNS(self::$nameSpace, "entry"));
>> -
>>                        // Atom fields
>> -                       $this->addNode($entry, 'title', $requestType.'
>> entry for '.$authorName);
>> +                       $this->addNode($entry, 'title', $requestType . '
>> entry for ' . $authorName);
>>                        $author = $this->addNode($entry, 'author');
>>                        $this->addNode($author, 'uri', $guid);
>>                        $this->addNode($author, 'name', $authorName);
>>                        $this->addNode($entry, 'id', $guid);
>>                        $this->addNode($entry, 'updated', $updatedAtom);
>>                        $content = $this->addNode($entry, 'content', '',
>> array('type' => 'application/xml'));
>> -
>>                        // addData loops through the responseItem data
>> recursively creating a matching XML structure
>>                        $this->addData($content, $requestType, $data,
>> self::$osNameSpace);
>>                }
>>                $xml = $doc->saveXML();
>> -               if (self::$includeOsearch && $responseItem->getResponse()
>> instanceof RestFulCollection) {
>> +               if ($responseItem->getResponse() instanceof
>> RestFulCollection) {
>>                        //FIXME dirty hack until i find a way to add
>> multiple name spaces using DomXML functions
>> -                       $xml = str_replace('<feed xmlns="
>> http://www.w3.org/2005/Atom";>', '<feed xmlns="http://www.w3.org/2005/Atom";
>> xmlos:osearch="http://a9.com/-/spec/opensearch/1.1";>' ,$xml);
>> +                       $xml = str_replace('<feed xmlns="
>> http://www.w3.org/2005/Atom";>', '<feed xmlns="http://www.w3.org/2005/Atom";
>> xmlns:osearch="http://a9.com/-/spec/opensearch/1.1";>', $xml);
>>                }
>>                echo $xml;
>>        }
>>
>>        function outputBatch(Array $responses, SecurityToken $token)
>>        {
>> -               //TODO once we support spec compliance batching, this
>> needs to be added too
>> +               $this->boundryHeaders();
>> +               foreach ($responses as $response) {
>> +                       $request = $response['request'];
>> +                       $response = $response['response'];
>> +                       // output buffering supports multiple levels of
>> it.. it's a nice feature to abuse :)
>> +                       ob_start();
>> +                       $this->outputResponse($response, $request);
>> +                       $part = ob_get_contents();
>> +                       ob_end_clean();
>> +                       $this->outputPart($part, $response->getError());
>> +               }
>>        }
>>
>>        /**
>> @@ -174,11 +178,9 @@
>>         */
>>        private function addPagingFields($entry, $startIndex,
>> $itemsPerPage, $totalResults)
>>        {
>> -               if (self::$includeOsearch) {
>> -                       $this->addNode($entry, 'osearch:totalResults',
>> $totalResults);
>> -                       $this->addNode($entry, 'osearch:startIndex',
>> $startIndex ? $startIndex : '0');
>> -                       $this->addNode($entry, 'osearch:itemsPerPage',
>> $itemsPerPage);
>> -               }
>> +               $this->addNode($entry, 'osearch:totalResults',
>> $totalResults);
>> +               $this->addNode($entry, 'osearch:startIndex', $startIndex ?
>> $startIndex : '0');
>> +               $this->addNode($entry, 'osearch:itemsPerPage',
>> $itemsPerPage);
>>                // Create a 'next' link based on our current url if this is
>> a pageable collection & there is more to display
>>                if (($startIndex + $itemsPerPage) < $totalResults) {
>>                        $nextStartIndex = ($startIndex + $itemsPerPage) -
>> 1;
>> @@ -255,6 +257,9 @@
>>                                        }
>>                                        $this->addData($newElement, $key,
>> $val);
>>                                } else {
>> +                                       if (is_numeric($key)) {
>> +                                               $key = is_object($val) ?
>> get_class($val) : $key = $name;
>> +                                       }
>>
>>                                        $elm =
>> $newElement->appendChild($this->doc->createElement($key));
>>
>>  $elm->appendChild($this->doc->createTextNode($val));
>>                                }
>>
>> Modified:
>> incubator/shindig/trunk/php/src/social-api/converters/OutputConverter.php
>> URL:
>> http://svn.apache.org/viewvc/incubator/shindig/trunk/php/src/social-api/converters/OutputConverter.php?rev=678068&r1=678067&r2=678068&view=diff
>>
>> ==============================================================================
>> ---
>> incubator/shindig/trunk/php/src/social-api/converters/OutputConverter.php
>> (original)
>> +++
>> incubator/shindig/trunk/php/src/social-api/converters/OutputConverter.php
>> Fri Jul 18 15:47:41 2008
>> @@ -22,6 +22,50 @@
>>  *
>>  */
>> abstract class OutputConverter {
>> +       private $boundry;
>> +
>>        abstract function outputResponse(ResponseItem $responseItem,
>> RestRequestItem $requestItem);
>>        abstract function outputBatch(Array $responses, SecurityToken
>> $token);
>> +
>> +       /**
>> +        * Output the multipart/mixed headers and returns the boundry
>> token used
>> +        *
>> +        */
>> +       public function boundryHeaders()
>> +       {
>> +               $this->boundry = '--batch-'.md5(rand(0,32000));
>> +               header("HTTP/1.1 200 OK", true);
>> +               header("Content-Type: multipart/mixed;
>> boundary=$this->boundry", true);
>> +       }
>> +
>> +       public function outputPart($part, $code)
>> +       {
>> +               $boundryHeader = "{$this->boundry}\n".
>> +                               "Content-Type:
>> application/http;version=1.1\n".
>> +                               "Content-Transfer-Encoding: binary\n\n";
>> +               echo $boundryHeader;
>> +               switch ($code) {
>> +                       case BAD_REQUEST:
>> +                               $code = '400 Bad Request';
>> +                               break;
>> +                       case UNAUTHORIZED:
>> +                               $code = '401 Unauthorized';
>> +                               break;
>> +                       case FORBIDDEN:
>> +                               $code = '403 Forbidden';
>> +                               break;
>> +                       case FORBIDDEN:
>> +                               $code = '404 Not Found';
>> +                               break;
>> +                       case NOT_IMPLEMENTED:
>> +                               $code = '501 Not Implemented';
>> +                               break;
>> +                       case INTERNAL_ERROR:
>> +                       default:
>> +                               $code = '200 OK';
>> +                               break;
>> +               }
>> +               echo "$code\n\n";
>> +               echo $part."\n";
>> +       }
>> }
>>
>> Modified:
>> incubator/shindig/trunk/php/src/social-api/converters/OutputJsonConverter.php
>> URL:
>> http://svn.apache.org/viewvc/incubator/shindig/trunk/php/src/social-api/converters/OutputJsonConverter.php?rev=678068&r1=678067&r2=678068&view=diff
>>
>> ==============================================================================
>> ---
>> incubator/shindig/trunk/php/src/social-api/converters/OutputJsonConverter.php
>> (original)
>> +++
>> incubator/shindig/trunk/php/src/social-api/converters/OutputJsonConverter.php
>> Fri Jul 18 15:47:41 2008
>> @@ -30,6 +30,17 @@
>>
>>        function outputBatch(Array $responses, SecurityToken $token)
>>        {
>> +               $this->boundryHeaders();
>> +               foreach ($responses as $response) {
>> +                       $request = $response['request'];
>> +                       $response = $response['response'];
>> +                       $part = json_encode($response);
>> +                       $this->outputPart($part, $response->getError());
>> +               }
>> +       }
>> +
>> +       function outputJsonBatch(Array $responses, SecurityToken $token)
>> +       {
>>                echo json_encode(array("responses" => $responses, "error"
>> => false));
>>        }
>> }
>>
>> Modified:
>> incubator/shindig/trunk/php/src/social-api/dataservice/PeopleHandler.php
>> URL:
>> http://svn.apache.org/viewvc/incubator/shindig/trunk/php/src/social-api/dataservice/PeopleHandler.php?rev=678068&r1=678067&r2=678068&view=diff
>>
>> ==============================================================================
>> ---
>> incubator/shindig/trunk/php/src/social-api/dataservice/PeopleHandler.php
>> (original)
>> +++
>> incubator/shindig/trunk/php/src/social-api/dataservice/PeopleHandler.php Fri
>> Jul 18 15:47:41 2008
>> @@ -20,7 +20,9 @@
>> class PeopleHandler extends DataRequestHandler {
>>        private $service;
>>        private static $PEOPLE_PATH =
>> "/people/{userId}/{groupId}/{personId}";
>> -       protected static $DEFAULT_PERSON_FIELDS = array("id", "name",
>> "thumbnailUrl");
>> +       //FIXME change this back to array("id", "name", "thumbnailUrl")
>> once the dust settles
>> +       // on the spec discussion related to this
>> +       protected static $DEFAULT_PERSON_FIELDS = array('all' => 'all');
>>
>>        public function __construct()
>>        {
>>
>> Modified:
>> incubator/shindig/trunk/php/src/social-api/dataservice/RestRequestItem.php
>> URL:
>> http://svn.apache.org/viewvc/incubator/shindig/trunk/php/src/social-api/dataservice/RestRequestItem.php?rev=678068&r1=678067&r2=678068&view=diff
>>
>> ==============================================================================
>> ---
>> incubator/shindig/trunk/php/src/social-api/dataservice/RestRequestItem.php
>> (original)
>> +++
>> incubator/shindig/trunk/php/src/social-api/dataservice/RestRequestItem.php
>> Fri Jul 18 15:47:41 2008
>> @@ -45,12 +45,12 @@
>>
>>        public function createRequestItemWithRequest($request, $token)
>>        {
>> -               $this->url = $request->url;
>> -               $this->parameters =
>> $this->createParameterMap($request->url);
>> +               $this->url = $request['url'];
>> +               $this->parameters =
>> $this->createParameterMap($request['url']);
>>                $this->token = $token;
>> -               $this->method = $request->method;
>> -               if (isset($request->postData)) {
>> -                       $this->postData = $request->postData;
>> +               $this->method = $request['method'];
>> +               if (isset($request['postData'])) {
>> +                       $this->postData = $request['postData'];
>>                }
>>        }
>>
>>
>> Modified: incubator/shindig/trunk/php/src/social-api/http/RestServlet.php
>> URL:
>> http://svn.apache.org/viewvc/incubator/shindig/trunk/php/src/social-api/http/RestServlet.php?rev=678068&r1=678067&r2=678068&view=diff
>>
>> ==============================================================================
>> --- incubator/shindig/trunk/php/src/social-api/http/RestServlet.php
>> (original)
>> +++ incubator/shindig/trunk/php/src/social-api/http/RestServlet.php Fri
>> Jul 18 15:47:41 2008
>> @@ -51,8 +51,6 @@
>> require 'src/social-api/converters/OutputAtomConverter.php';
>> require 'src/social-api/converters/OutputJsonConverter.php';
>>
>> -//FIXME Delete should respond with a 204 No Content to indicate success
>> -
>> class RestException extends Exception {}
>>
>> /*
>> @@ -64,42 +62,80 @@
>> define('FORBIDDEN', "forbidden");
>> define('BAD_REQUEST', "badRequest");
>> define('INTERNAL_ERROR', "internalError");
>> +//FIXME Delete should respond with a 204 No Content to indicate success
>>
>> class RestServlet extends HttpServlet {
>>
>> +       // The json Batch Route is used by the gadgets
>>        private static $JSON_BATCH_ROUTE = "jsonBatch";
>> +       // The Batch Proxy route is used the one defined in the RESTful
>> API specification
>> +       private static $BATCH_PROXY_ROUTE = "batchProxy";
>> +
>> +       public function doGet()
>> +       {
>> +               $this->doPost('GET');
>> +       }
>>
>> +       public function doPut()
>> +       {
>> +               $this->doPost('PUT');
>> +       }
>> +
>> +       public function doDelete()
>> +       {
>> +               $this->doPost('DELETE');
>> +       }
>> +
>>        public function doPost($method = 'POST')
>>        {
>> -               $this->setNoCache(true);
>> -               // if oauth, create a token from it's values instead of
>> one based on $_get['st']/$_post['st']
>> -               // NOTE : if no token is provided an anonymous one is
>> created (owner = viewer = appId = modId = 0)
>> -               // keep this in mind when creating your data services..
>> -               $token = $this->getSecurityToken();
>> -               $outputFormat = $this->getOutputFormat();
>> -               switch ($outputFormat) {
>> -                       case 'json':
>> -                               $this->setContentType('application/json');
>> -                               $outputConverter = new
>> OutputJsonConverter();
>> -                               break;
>> -                       case 'atom':
>> -
>> $this->setContentType('application/atom+xml');
>> -                               $outputConverter = new
>> OutputAtomConverter();
>> -                               break;
>> -                       default:
>> -                               $this->outputError(new
>> ResponseItem(NOT_IMPLEMENTED, "Invalid output format"));
>> -                               break;
>> -               }
>> -               if ($this->isBatchUrl()) {
>> -                       $responses = $this->handleBatchRequest($token);
>> -                       $outputConverter->outputBatch($responses, $token);
>> -               } else {
>> -                       $response = $this->handleSingleRequest($token,
>> $method);
>> -
>> $outputConverter->outputResponse($response['response'],
>> $response['request']);
>> +               try {
>> +                       $this->setNoCache(true);
>> +                       // if oauth, create a token from it's values
>> instead of one based on $_get['st']/$_post['st']
>> +                       // NOTE : if no token is provided an anonymous one
>> is created (owner = viewer = appId = modId = 0)
>> +                       // keep this in mind when creating your data
>> services..
>> +                       $token = $this->getSecurityToken();
>> +                       $outputFormat = $this->getOutputFormat();
>> +                       switch ($outputFormat) {
>> +                               case 'json':
>> +
>> $this->setContentType('application/json');
>> +                                       $outputConverter = new
>> OutputJsonConverter();
>> +                                       break;
>> +                               case 'atom':
>> +
>> $this->setContentType('application/atom+xml');
>> +                                       $outputConverter = new
>> OutputAtomConverter();
>> +                                       break;
>> +                               default:
>> +                                       $this->outputError(new
>> ResponseItem(NOT_IMPLEMENTED, "Invalid output format"));
>> +                                       break;
>> +                       }
>> +                       if ($this->isJsonBatchUrl()) {
>> +                               // custom json batch format used by the
>> gadgets
>> +                               $responses =
>> $this->handleJsonBatchRequest($token);
>> +
>> $outputConverter->outputJsonBatch($responses, $token);
>> +                       } elseif ($this->isBatchProxyUrl()) {
>> +                               // spec compliant batch proxy
>> +                               $this->noHeaders = true;
>> +                               $responses =
>> $this->handleBatchProxyRequest($token);
>> +                               $outputConverter->outputBatch($responses,
>> $token);
>> +                       } else {
>> +                               // single rest request
>> +                               $response = $this->handleRequest($token,
>> $method);
>> +
>> $outputConverter->outputResponse($response['response'],
>> $response['request']);
>> +                       }
>> +               } catch (Exception $e) {
>> +                       header("HTTP/1.0 500 Internal Server Error");
>> +                       echo "<html><body><h1>500 Internal Server
>> Error</h1>";
>> +                       if (Config::get('debug')) {
>> +                               echo "Message: ".$e->getMessage()."<br
>> />\n";
>> +                               echo "<pre>\n";
>> +                               print_r(debug_backtrace());
>> +                               echo "\n</pre>";
>> +                       }
>> +                       echo "</body></html>";
>>                }
>>        }
>>
>> -       private function handleSingleRequest($token, $method)
>> +       private function handleRequest($token, $method)
>>        {
>>                $params = $this->getListParams();
>>                $requestItem = new RestRequestItem();
>> @@ -109,14 +145,115 @@
>>                $responseItem = $this->getResponseItem($requestItem);
>>                return array('request' => $requestItem, 'response' =>
>> $responseItem);
>>        }
>> -
>> -       private function getRouteFromParameter($pathInfo)
>> +
>> +       private function handleJsonBatchRequest($token)
>>        {
>> -               $pathInfo = substr($pathInfo, 1);
>> -               $indexOfNextPathSeparator = strpos($pathInfo, "/");
>> -               return $indexOfNextPathSeparator != - 1 ?
>> substr($pathInfo, 0, $indexOfNextPathSeparator) : $pathInfo;
>> +               // we support both a raw http post (without
>> application/x-www-form-urlencoded headers) like java does
>> +               // and a more php / curl safe version of a form post with
>> 'request' as the post field that holds the request json data
>> +               if (isset($GLOBALS['HTTP_RAW_POST_DATA']) ||
>> isset($_POST['request'])) {
>> +                       $requests = $this->getRequestParams();
>> +                       $responses = array();
>> +                       foreach ($requests as $key => $value) {
>> +                               $requestItem = new RestRequestItem();
>> +
>> $requestItem->createRequestItemWithRequest($value, $token);
>> +                               $responses[$key] =
>> $this->getResponseItem($requestItem);
>> +                       }
>> +                       return $responses;
>> +               } else {
>> +                       throw new Exception("No post data set");
>> +               }
>> +       }
>> +
>> +       private function handleBatchProxyRequest($token)
>> +       {
>> +               // Is this is a multipath/mixed post? Check content type:
>> +               if (isset($GLOBALS['HTTP_RAW_POST_DATA']) &&
>> strpos($_SERVER['CONTENT_TYPE'], 'multipart/mixed') !== false &&
>> strpos($_SERVER['CONTENT_TYPE'],'boundary=') !== false) {
>> +                       // Ok looks swell, see what our boundry is..
>> +                       $boundry = substr($_SERVER['CONTENT_TYPE'],
>> strpos($_SERVER['CONTENT_TYPE'],'boundary=') + strlen('boundary='));
>> +                       // Split up requests per boundry
>> +                       $requests = explode($boundry,
>> $GLOBALS['HTTP_RAW_POST_DATA']);
>> +                       $responses = array();
>> +                       foreach ($requests as $request) {
>> +                               $request = trim($request);
>> +                               if (!empty($request)) {
>> +                                       // extractBatchRequest() does the
>> magic parsing of the raw post data to a meaninful request array
>> +                                       $request =
>> $this->extractBatchRequest($request);
>> +                                       $requestItem = new
>> RestRequestItem();
>> +
>> $requestItem->createRequestItemWithRequest($request, $token);
>> +                                       $responses[] = array('request' =>
>> $requestItem, 'response' => $this->getResponseItem($requestItem));
>> +                               }
>> +                       }
>> +               } else {
>> +                       $this->outputError(new ResponseItem(BAD_REQUEST,
>> "Invalid multipart/mixed request"));
>> +               }
>> +               return $responses;
>>        }
>>
>> +       private function extractBatchRequest($request)
>> +       {
>> +               /* Multipart request is formatted like:
>> +                * -batch-a73hdj3dy3mm347ddjjdf
>> +                * Content-Type: application/http;version=1.1
>> +                * Content-Transfer-Encoding: binary
>> +                *
>> +                * GET
>> /people/@me/@friends?startPage=5&count=10&format=json
>> +                * Host: api.example.org
>> +                * If-None-Match: "837dyfkdi39df"
>> +                *
>> +                * but we only want to have the last bit (the actual
>> request), this filters that down first
>> +                */
>> +               $emptyFound = false;
>> +               $requestLines = explode("\n", $request);
>> +               $request = '';
>> +               foreach ($requestLines as $line) {
>> +                       if ($emptyFound) {
>> +                               $request .= $line."\n";
>> +                       } elseif (empty($line)) {
>> +                               $emptyFound = true;
>> +                       }
>> +               }
>> +               // Now that we have the basic request in $request, split
>> that up again & parse it into a meaningful representation
>> +               $firstFound = $emptyFound = false;
>> +               $requestLines = explode("\n", $request);
>> +               $request = array();
>> +               $request['headers'] = array();
>> +               $request['postData'] = '';
>> +               foreach ($requestLines as $line) {
>> +                       if (!$firstFound) {
>> +                               $firstFound = true;
>> +                               $parts = explode(' ', trim($line));
>> +                               if (count($parts) != 2) {
>> +                                       throw new Exception("Mallshaped
>> request uri in multipart block");
>> +                               }
>> +                               $request['method'] =
>> strtoupper(trim($parts[0]));
>> +                               // cut it down to an actual meaningful url
>> without the prefix/social/rest part right away
>> +                               $request['url'] = substr(trim($parts[1]),
>> strlen(Config::get('web_prefix') . '/social/rest'));
>> +                       } elseif (!$emptyFound && !empty($line)) {
>> +                               // convert the key to the PHP
>> 'CONTENT_TYPE' style naming convention.. it's ugly but consitent
>> +                               $key = str_replace('-', '_',
>> strtoupper(trim(substr($line, 0, strpos($line, ':')))));
>> +                               $val = trim(substr($line, strpos($line,
>> ':') + 1));
>> +                               $request['headers'][$key] = $val;
>> +                       } elseif (!$emptyFound && empty($line)) {
>> +                               $emptyFound = true;
>> +                       } else {
>> +                               if (get_magic_quotes_gpc()) {
>> +                                       $line = stripslashes($line);
>> +                               }
>> +                               $request['postData'] .= $line."\n";
>> +                       }
>> +               }
>> +               if (empty($request['postData'])) {
>> +                       // don't trip the requestItem into thinking there
>> is postData when there's not
>> +                       unset($request['postData']);
>> +               } else {
>> +                       // if there was a post data blob present, decode
>> it into an array, the format is based on the
>> +                       // content type header, which is either
>> application/json or
>> +                       $format =
>> isset($request['headers']['CONTENT_TYPE']) &&
>> strtolower($request['headers']['CONTENT_TYPE']) == 'application/atom+xml' ?
>> 'atom' : 'json';
>> +                       $request['postData'] =
>> $this->decodeRequests($request['postData'], $format);
>> +               }
>> +               return $request;
>> +       }
>> +
>>        private function getResponseItem(RestRequestItem $requestItem)
>>        {
>>                $path =
>> $this->getRouteFromParameter($requestItem->getUrl());
>> @@ -140,12 +277,26 @@
>>                        $class = new $class(null);
>>                        $response = $class->handleMethod($requestItem);
>>                }
>> -               if ($response->getError() != null && !$this->isBatchUrl())
>> {
>> +               if ($response->getError() != null &&
>> !$this->isJsonBatchUrl() && !$this->isBatchProxyUrl()) {
>>                        // Can't use http error codes in batch mode,
>> instead we return the error code in the response item
>>                        $this->outputError($response);
>>                }
>>                return $response;
>>        }
>> +
>> +       private function decodeRequests($requestParam, $format = 'json')
>> +       {
>> +               // temp hack until i know what the intended way to detect
>> format is
>> +               if ($format == 'json') {
>> +                       return json_decode($requestParam, true);
>> +               } elseif ($format == 'atom') {
>> +                       $xml = simplexml_load_string($requestParam);
>> +                       print_r($xml);
>> +                       return $xml;
>> +               } else {
>> +                       throw Exception("Invalid or unsupported input
>> format");
>> +               }
>> +       }
>>
>>        private function getRequestParams()
>>        {
>> @@ -154,44 +305,14 @@
>>                if (get_magic_quotes_gpc()) {
>>                        $requestParam = stripslashes($requestParam);
>>                }
>> -               $requests = json_decode($requestParam);
>> -               if ($requests == (isset($GLOBALS['HTTP_RAW_POST_DATA']) ?
>> $GLOBALS['HTTP_RAW_POST_DATA'] : $post)) {
>> -                       return new ResponseItem(BAD_REQUEST, "Malformed
>> json string");
>> -               }
>> -               return $requests;
>> -       }
>> -
>> -       private function handleBatchRequest($token)
>> -       {
>> -               // we support both a raw http post (without
>> application/x-www-form-urlencoded headers) like java does
>> -               // and a more php / curl safe version of a form post with
>> 'request' as the post field that holds the request json data
>> -               if (isset($GLOBALS['HTTP_RAW_POST_DATA']) ||
>> isset($_POST['request'])) {
>> -                       $requests = $this->getRequestParams();
>> -                       $responses = array();
>> -                       foreach ($requests as $key => $value) {
>> -                               $requestItem = new RestRequestItem();
>> -
>> $requestItem->createRequestItemWithRequest($value, $token);
>> -                               $responses[$key] =
>> $this->getResponseItem($requestItem);
>> -                       }
>> -                       return $responses;
>> -               } else {
>> -                       throw new Exception("No post data set");
>> -               }
>> -       }
>> -
>> -       public function doGet()
>> -       {
>> -               $this->doPost('GET');
>> -       }
>> -
>> -       public function doPut()
>> -       {
>> -               $this->doPost('PUT');
>> +               return $this->decodeRequests($requestParam);
>>        }
>>
>> -       public function doDelete()
>> +       private function getRouteFromParameter($pathInfo)
>>        {
>> -               $this->doPost('DELETE');
>> +               $pathInfo = substr($pathInfo, 1);
>> +               $indexOfNextPathSeparator = strpos($pathInfo, "/");
>> +               return $indexOfNextPathSeparator != - 1 ?
>> substr($pathInfo, 0, $indexOfNextPathSeparator) : $pathInfo;
>>        }
>>
>>        private function outputError(ResponseItem $response)
>> @@ -217,7 +338,6 @@
>>                        default:
>>                                $code = '500 Internal Server Error';
>>                                break;
>> -
>>                }
>>                header("HTTP/1.0 $code", true);
>>                echo "$code - $errorMessage";
>> @@ -265,8 +385,13 @@
>>                return substr($_SERVER["REQUEST_URI"],
>> strlen(Config::get('web_prefix') . '/social/rest'));
>>        }
>>
>> -       public function isBatchUrl()
>> +       public function isJsonBatchUrl()
>>        {
>>                return strrpos($_SERVER["REQUEST_URI"],
>> RestServlet::$JSON_BATCH_ROUTE) > 0;
>>        }
>> +
>> +       public function isBatchProxyUrl()
>> +       {
>> +               return strrpos($_SERVER["REQUEST_URI"],
>> RestServlet::$BATCH_PROXY_ROUTE) > 0;
>> +       }
>> }
>>
>> Modified:
>> incubator/shindig/trunk/php/src/social-api/samplecontainer/BasicAppDataService.php
>> URL:
>> http://svn.apache.org/viewvc/incubator/shindig/trunk/php/src/social-api/samplecontainer/BasicAppDataService.php?rev=678068&r1=678067&r2=678068&view=diff
>>
>> ==============================================================================
>> ---
>> incubator/shindig/trunk/php/src/social-api/samplecontainer/BasicAppDataService.php
>> (original)
>> +++
>> incubator/shindig/trunk/php/src/social-api/samplecontainer/BasicAppDataService.php
>> Fri Jul 18 15:47:41 2008
>> @@ -85,7 +85,7 @@
>>                switch($groupId->getType()) {
>>                        case 'self':
>>                                foreach ($fields as $key) {
>> -                                       $value = isset($values->$key) ?
>> $values->$key : (@isset($values[$key]) ? @$values[$key] : null);
>> +                                       $value = isset($values[$key]) ?
>> @$values[$key] : null;
>>
>>  XmlStateFileFetcher::get()->setAppData($userId->getUserId($token), $key,
>> $value);
>>                                }
>>                                break;
>>
>> Modified:
>> incubator/shindig/trunk/php/src/social-api/samplecontainer/BasicPeopleService.php
>> URL:
>> http://svn.apache.org/viewvc/incubator/shindig/trunk/php/src/social-api/samplecontainer/BasicPeopleService.php?rev=678068&r1=678067&r2=678068&view=diff
>>
>> ==============================================================================
>> ---
>> incubator/shindig/trunk/php/src/social-api/samplecontainer/BasicPeopleService.php
>> (original)
>> +++
>> incubator/shindig/trunk/php/src/social-api/samplecontainer/BasicPeopleService.php
>> Fri Jul 18 15:47:41 2008
>> @@ -76,7 +76,7 @@
>>                                if ($id == $token->getOwnerId()) {
>>                                        $person->setIsOwner(true);
>>                                }
>> -                               if (is_array($profileDetails) &&
>> count($profileDetails)) {
>> +                               if (is_array($profileDetails) &&
>> count($profileDetails) && !in_array('all', $profileDetails)) {
>>                                        $newPerson = array();
>>                                        $newPerson['isOwner'] =
>> $person->isOwner;
>>                                        $newPerson['isViewer'] =
>> $person->isViewer;
>>
>>
>


-- 
.-. --- .--. ..-
R o p u

Reply via email to