/** * Send the HTTP request and return an HTTP response object * * @param string $method * @return Microsoft_Http_Response * @throws Microsoft_Http_Client_Exception */ public function request($method = null) { if (!$this->uri instanceof Microsoft_Uri_Http) { /** @see Microsoft_Http_Client_Exception */ require_once 'Microsoft/Http/Client/Exception.php'; throw new Microsoft_Http_Client_Exception('No valid URI has been passed to the client'); } if ($method) { $this->setMethod($method); } $this->redirectCounter = 0; $response = null; // Make sure the adapter is loaded if ($this->adapter == null) { $this->setAdapter($this->config['adapter']); } // Send the first request. If redirected, continue. do { // Clone the URI and add the additional GET parameters to it $uri = clone $this->uri; if (!empty($this->paramsGet)) { $query = $uri->getQuery(); if (!empty($query)) { $query .= '&'; } $query .= http_build_query($this->paramsGet, null, '&'); $uri->setQuery($query); } $body = $this->_prepareBody(); $headers = $this->_prepareHeaders(); // check that adapter supports streaming before using it if (is_resource($body) && !$this->adapter instanceof Microsoft_Http_Client_Adapter_Stream) { /** @see Microsoft_Http_Client_Exception */ require_once 'Microsoft/Http/Client/Exception.php'; throw new Microsoft_Http_Client_Exception('Adapter does not support streaming'); } // Open the connection, send the request and read the response $this->adapter->connect($uri->getHost(), $uri->getPort(), $uri->getScheme() == 'https' ? true : false); if ($this->config['output_stream']) { if ($this->adapter instanceof Microsoft_Http_Client_Adapter_Stream) { $stream = $this->_openTempStream(); $this->adapter->setOutputStream($stream); } else { /** @see Microsoft_Http_Client_Exception */ require_once 'Microsoft/Http/Client/Exception.php'; throw new Microsoft_Http_Client_Exception('Adapter does not support streaming'); } } $this->last_request = $this->adapter->write($this->method, $uri, $this->config['httpversion'], $headers, $body); $response = $this->adapter->read(); if (!$response) { /** @see Microsoft_Http_Client_Exception */ require_once 'Microsoft/Http/Client/Exception.php'; throw new Microsoft_Http_Client_Exception('Unable to read response, or response is empty'); } if ($this->config['output_stream']) { rewind($stream); // cleanup the adapter $this->adapter->setOutputStream(null); $response = Microsoft_Http_Response_Stream::fromStream($response, $stream); $response->setStreamName($this->_stream_name); if (!is_string($this->config['output_stream'])) { // we used temp name, will need to clean up $response->setCleanup(true); } } else { $response = Microsoft_Http_Response::fromString($response); } if ($this->config['storeresponse']) { $this->last_response = $response; } // Load cookies into cookie jar if (isset($this->cookiejar)) { $this->cookiejar->addCookiesFromResponse($response, $uri); } // If we got redirected, look for the Location header if ($response->isRedirect() && ($location = $response->getHeader('location'))) { // Check whether we send the exact same request again, or drop the parameters // and send a GET request if ($response->getStatus() == 303 || !$this->config['strictredirects'] && ($response->getStatus() == 302 || $response->getStatus() == 301)) { $this->resetParameters(); $this->setMethod(self::GET); } // If we got a well formed absolute URI if (Microsoft_Uri_Http::check($location)) { $this->setHeaders('host', null); $this->setUri($location); } else { // Split into path and query and set the query if (strpos($location, '?') !== false) { list($location, $query) = explode('?', $location, 2); } else { $query = ''; } $this->uri->setQuery($query); // Else, if we got just an absolute path, set it if (strpos($location, '/') === 0) { $this->uri->setPath($location); // Else, assume we have a relative path } else { // Get the current path directory, removing any trailing slashes $path = $this->uri->getPath(); $path = rtrim(substr($path, 0, strrpos($path, '/')), "/"); $this->uri->setPath($path . '/' . $location); } } ++$this->redirectCounter; } else { // If we didn't get any location, stop redirecting break; } } while ($this->redirectCounter < $this->config['maxredirects']); return $response; }
/** * This function will extract the changesets from raw batch response and generate * a list of Microsoft_Http_Response objects. * * @param string $rawHttpBatchResponse * @param string changesetBoundary * @return array */ protected static function ExtractHttpResponses($httpBatchResponse, $changesetBoundary) { $httpResponses = array(); $parts = explode("--" . $changesetBoundary, $httpBatchResponse); $count = count($parts); if ($count < 2) { return $httpResponses; } unset($parts[0]); unset($parts[$count - 1]); foreach ($parts as $changeSet) { $subParts = preg_split('|(?:\\r?\\n){2}|m', $changeSet, 2); $httpResponses[] = Microsoft_Http_Response::fromString($subParts[1]); } return $httpResponses; }
/** * Retrieve entities from table * * @param string $tableName|Microsoft_WindowsAzure_Storage_TableEntityQuery Table name -or- Microsoft_WindowsAzure_Storage_TableEntityQuery instance * @param string $filter Filter condition (not applied when $tableName is a Microsoft_WindowsAzure_Storage_TableEntityQuery instance) * @param string $entityClass Entity class name * @param string $nextPartitionKey Next partition key, used for listing entities when total amount of entities is > 1000. * @param string $nextRowKey Next row key, used for listing entities when total amount of entities is > 1000. * @return array Array of Microsoft_WindowsAzure_Storage_TableEntity * @throws Microsoft_WindowsAzure_Exception */ public function retrieveEntities($tableName = '', $filter = '', $entityClass = 'Microsoft_WindowsAzure_Storage_DynamicTableEntity', $nextPartitionKey = null, $nextRowKey = null) { if ($tableName === '') { throw new Microsoft_WindowsAzure_Exception('Table name is not specified.'); } if ($entityClass === '') { throw new Microsoft_WindowsAzure_Exception('Entity class is not specified.'); } // Convenience... if (class_exists($filter)) { $entityClass = $filter; $filter = ''; } // Query string $queryString = ''; // Determine query if (is_string($tableName)) { // Option 1: $tableName is a string // Append parentheses if (strpos($tableName, '()') === false) { $tableName .= '()'; } // Build query $query = array(); // Filter? if ($filter !== '') { $query[] = '$filter=' . Microsoft_WindowsAzure_Storage_TableEntityQuery::encodeQuery($filter); } // Build queryString if (count($query) > 0) { $queryString = '?' . implode('&', $query); } } else { if (get_class($tableName) == 'Microsoft_WindowsAzure_Storage_TableEntityQuery') { // Option 2: $tableName is a Microsoft_WindowsAzure_Storage_TableEntityQuery instance // Build queryString $queryString = $tableName->assembleQueryString(true); // Change $tableName $tableName = $tableName->assembleFrom(true); } else { throw new Microsoft_WindowsAzure_Exception('Invalid argument: $tableName'); } } // Add continuation querystring parameters? if (!is_null($nextPartitionKey) && !is_null($nextRowKey)) { if ($queryString !== '') { $queryString .= '&'; } else { $queryString .= '?'; } $queryString .= 'NextPartitionKey=' . rawurlencode($nextPartitionKey) . '&NextRowKey=' . rawurlencode($nextRowKey); } // Perform request $response = null; if ($this->isInBatch() && $this->getCurrentBatch()->getOperationCount() == 0) { $this->getCurrentBatch()->enlistOperation($tableName, $queryString, Microsoft_Http_Client::GET, array(), true, null); $response = $this->getCurrentBatch()->commit(); // Get inner response (multipart) $innerResponse = $response->getBody(); $innerResponse = substr($innerResponse, strpos($innerResponse, 'HTTP/1.1 200 OK')); $innerResponse = substr($innerResponse, 0, strpos($innerResponse, '--batchresponse')); $response = Microsoft_Http_Response::fromString($innerResponse); } else { $response = $this->_performRequest($tableName, $queryString, Microsoft_Http_Client::GET, array(), true, null); } if ($response->isSuccessful()) { // Parse result $result = $this->_parseResponse($response); if (!$result) { return array(); } $entries = null; if ($result->entry) { if (count($result->entry) > 1) { $entries = $result->entry; } else { $entries = array($result->entry); } } else { // This one is tricky... If we have properties defined, we have an entity. $properties = $result->xpath('//m:properties'); if ($properties) { $entries = array($result); } else { return array(); } } // Create return value $returnValue = array(); foreach ($entries as $entry) { // Parse properties $properties = $entry->xpath('.//m:properties'); $properties = $properties[0]->children('http://schemas.microsoft.com/ado/2007/08/dataservices'); // Create entity $entity = new $entityClass('', ''); $entity->setAzureValues((array) $properties, $this->_throwExceptionOnMissingData); // If we have a Microsoft_WindowsAzure_Storage_DynamicTableEntity, make sure all property types are set if ($entity instanceof Microsoft_WindowsAzure_Storage_DynamicTableEntity) { foreach ($properties as $key => $value) { $attributes = $value->attributes('http://schemas.microsoft.com/ado/2007/08/dataservices/metadata'); $type = (string) $attributes['type']; if ($type !== '') { $entity->setAzureProperty($key, (string) $value, $type); } } } // Update etag $etag = $entry->attributes('http://schemas.microsoft.com/ado/2007/08/dataservices/metadata'); $etag = (string) $etag['etag']; $entity->setEtag($etag); // Add to result $returnValue[] = $entity; } // More entities? if (!is_null($response->getHeader('x-ms-continuation-NextPartitionKey')) && !is_null($response->getHeader('x-ms-continuation-NextRowKey'))) { if (strpos($queryString, '$top') === false) { $returnValue = array_merge($returnValue, $this->retrieveEntities($tableName, $filter, $entityClass, $response->getHeader('x-ms-continuation-NextPartitionKey'), $response->getHeader('x-ms-continuation-NextRowKey'))); } } // Return return $returnValue; } else { throw new Microsoft_WindowsAzure_Exception($this->_getErrorMessage($response, 'Resource could not be accessed.')); } }
/** * Perform request * * @param $httpVerb Http verb to use in the request * @param $url Url to request * @param $variables Array of key-value pairs to use in the request * @param $headers Array of key-value pairs to use as additional headers * @param $rawBody Raw body to send to server * @return Microsoft_Http_Response */ public function request($httpVerb, $url, $variables = array(), $headers = array(), $rawBody = null) { // Create a new cURL instance $curlHandle = curl_init(); curl_setopt($curlHandle, CURLOPT_USERAGENT, $this->_userAgent); curl_setopt($curlHandle, CURLOPT_FOLLOWLOCATION, true); curl_setopt($curlHandle, CURLOPT_TIMEOUT, 120); // Set URL curl_setopt($curlHandle, CURLOPT_URL, $url); // Set HTTP parameters (version and request method) curl_setopt($curlHandle, CURL_HTTP_VERSION_1_1, true); switch ($httpVerb) { case Microsoft_Http_Transport::VERB_GET: curl_setopt($curlHandle, CURLOPT_HTTPGET, true); break; case Microsoft_Http_Transport::VERB_POST: curl_setopt($curlHandle, CURLOPT_POST, true); break; /*case Microsoft_Http_Transport::VERB_PUT: curl_setopt($curlHandle, CURLOPT_PUT, true); break;*/ /*case Microsoft_Http_Transport::VERB_PUT: curl_setopt($curlHandle, CURLOPT_PUT, true); break;*/ case Microsoft_Http_Transport::VERB_HEAD: // http://stackoverflow.com/questions/770179/php-curl-head-request-takes-a-long-time-on-some-sites curl_setopt($curlHandle, CURLOPT_CUSTOMREQUEST, 'HEAD'); curl_setopt($curlHandle, CURLOPT_NOBODY, true); break; default: curl_setopt($curlHandle, CURLOPT_CUSTOMREQUEST, $httpVerb); break; } // Clear Content-Length header $headers["Content-Length"] = 0; // Ensure headers are returned curl_setopt($curlHandle, CURLOPT_HEADER, true); // Set proxy? if ($this->_useProxy) { curl_setopt($curlHandle, CURLOPT_PROXY, $this->_proxyUrl); curl_setopt($curlHandle, CURLOPT_PROXYPORT, $this->_proxyPort); curl_setopt($curlHandle, CURLOPT_PROXYUSERPWD, $this->_proxyCredentials); } // Ensure response is returned curl_setopt($curlHandle, CURLOPT_RETURNTRANSFER, true); // Set post fields / raw data // http://www.php.net/manual/en/function.curl-setopt.php#81161 if (!is_null($rawBody) || !is_null($variables) && count($variables) > 0) { if (!is_null($rawBody)) { unset($headers["Content-Length"]); $headers["Content-Length"] = strlen($rawBody); } curl_setopt($curlHandle, CURLOPT_POSTFIELDS, is_null($rawBody) ? $variables : $rawBody); } // Set Content-Type header if required if (!isset($headers["Content-Type"])) { $headers["Content-Type"] = ''; } // Disable Expect: 100-Continue // http://be2.php.net/manual/en/function.curl-setopt.php#82418 $headers["Expect"] = ''; // Add additional headers to cURL instance $curlHeaders = array(); foreach ($headers as $key => $value) { $curlHeaders[] = $key . ': ' . $value; } curl_setopt($curlHandle, CURLOPT_HTTPHEADER, $curlHeaders); // DEBUG: curl_setopt($curlHandle, CURLINFO_HEADER_OUT, true); // Execute request $rawResponse = curl_exec($curlHandle); $response = null; if ($rawResponse) { $response = Microsoft_Http_Response::fromString($rawResponse); // DEBUG: var_dump($url); // DEBUG: var_dump(curl_getinfo($curlHandle,CURLINFO_HEADER_OUT)); // DEBUG: var_dump($rawResponse); } else { throw new Microsoft_Http_Transport_Exception('cURL error occured during request for ' . $url . ': ' . curl_errno($curlHandle) . ' - ' . curl_error($curlHandle)); } curl_close($curlHandle); return $response; }