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

