laipower/wp-content/plugins/w3-total-cache/lib/Azure/MicrosoftAzureStorage/Common/Internal/ServiceRestProxy.php

620 lines
20 KiB
PHP

<?php
/**
* LICENSE: The MIT License (the "License")
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* https://github.com/azure/azure-storage-php/LICENSE
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
* PHP version 5
*
* @category Microsoft
* @package MicrosoftAzure\Storage\Common\Internal
* @author Azure Storage PHP SDK <dmsh@microsoft.com>
* @copyright 2016 Microsoft Corporation
* @license https://github.com/azure/azure-storage-php/LICENSE
* @link https://github.com/azure/azure-storage-php
*/
namespace MicrosoftAzure\Storage\Common\Internal;
use MicrosoftAzure\Storage\Common\ServiceException;
use MicrosoftAzure\Storage\Common\Internal\Resources;
use MicrosoftAzure\Storage\Common\Internal\Validate;
use MicrosoftAzure\Storage\Common\Internal\Utilities;
use MicrosoftAzure\Storage\Common\Internal\RetryMiddlewareFactory;
use GuzzleHttp\HandlerStack;
use GuzzleHttp\Middleware;
use GuzzleHttp\Client;
use GuzzleHttp\Psr7;
use GuzzleHttp\Psr7\Request;
use GuzzleHttp\Psr7\Response;
use GuzzleHttp\Psr7\Uri;
use GuzzleHttp\Promise\EachPromise;
/**
* Base class for all services rest proxies.
*
* @category Microsoft
* @package MicrosoftAzure\Storage\Common\Internal
* @author Azure Storage PHP SDK <dmsh@microsoft.com>
* @copyright 2016 Microsoft Corporation
* @license https://github.com/azure/azure-storage-php/LICENSE
* @version Release: 0.11.0
* @link https://github.com/azure/azure-storage-php
*/
class ServiceRestProxy extends RestProxy
{
/**
* @var string
*/
private $_accountName;
/**
*
* @var \Uri
*/
private $_psrUri;
/**
* @var array
*/
private $_options;
/**
* Initializes new ServiceRestProxy object.
*
* @param string $uri The storage account uri.
* @param string $accountName The name of the account.
* @param ISerializer $dataSerializer The data serializer.
* @param array $options Array of options for the service
*/
public function __construct($uri, $accountName, $dataSerializer, $options = [])
{
if ($uri[strlen($uri)-1] != '/') {
$uri = $uri . '/';
}
parent::__construct($dataSerializer, $uri);
$this->_accountName = $accountName;
$this->_psrUri = new \GuzzleHttp\Psr7\Uri($uri);
$this->_options = array_merge(array('http' => array()), $options);
}
/**
* Gets the account name.
*
* @return string
*/
public function getAccountName()
{
return $this->_accountName;
}
/**
* Filter the request using the filters. This is for users to create
* request.
* @param \GuzzleHttp\Psr7\Request $request The request to be filtered.
*
* @return \GuzzleHttp\Psr7\Request The filtered request.
*/
protected function requestWithFilter($request)
{
// Apply filters to the requests
foreach ($this->getFilters() as $filter) {
$request = $filter->handleRequest($request);
}
return $request;
}
/**
* Static helper function to create a usable client for the proxy.
* The clientOptions can contain the following keys that will affect
* the way retry handler is created and applied.
* handler: HandlerStack, if set, this function will not
* create a handler stack. It will still construct
* a default retry handler if not specified by the
* following parameters.
* have_retry_middleware: boolean, true if the handler is already specified
* in the handler stack.
* retry_middleware: Middleware, if specified this method will not create
* a default retry middle ware.
* @param array $clientOptions Added options for client.
*
* @return \GuzzleHttp\Client
*/
protected function createClient($clientOptions)
{
//If retry handler is not defined by the user, create a default
//handler.
$stack = null;
if (array_key_exists('handler', $this->_options['http'])) {
$stack = $this->_options['http']['handler'];
} elseif (array_key_exists('handler', $clientOptions)) {
$stack = $clientOptions['handler'];
} else {
$stack = HandlerStack::create();
$clientOptions['handler'] = $stack;
}
//If retry middle ware is specified, push it to the client.
//Otherwise use the default middle ware.
if (array_key_exists('have_retry_middleware', $clientOptions) &&
$clientOptions['have_retry_middleware'] == true) {
//do nothing
} elseif (array_key_exists('retry_middleware', $clientOptions)) {
//push the retry middleware to the handler stack.
$stack->push($clientOptions['retry_middleware']);
} else {
//construct the default retry middleware and push to the handler.
$stack->push(RetryMiddlewareFactory::create(), 'retry_middleware');
}
return (new \GuzzleHttp\Client(
array_merge(
$this->_options['http'],
array(
"defaults" => array(
"allow_redirects" => true,
"exceptions" => true,
"decode_content" => true,
),
'cookies' => true,
'verify' => false,
// For testing with Fiddler
//'proxy' => "localhost:8888",
),
$clientOptions
)
));
}
/**
* Send the requests concurrently. Number of concurrency can be modified
* by inserting a new key/value pair with the key 'number_of_concurrency'
* into the $clientOptions.
*
* @param array $requests An array holding all the
* initialized requests. If empty,
* the first batch will be created
* using the generator.
* @param callable $generator the generator function to
* generate request upon fullfilment
* @param int $expectedStatusCode The expected status code for each
* of the request.
* @param array $clientOptions an array of additional options
* for the client.
*
* @return array
*/
protected function sendConcurrent(
$requests,
$generator,
$expectedStatusCode,
$clientOptions = []
) {
//set the number of concurrency to default value if not defined
//in the array.
$numberOfConcurrency = Resources::NUMBER_OF_CONCURRENCY;
if (array_key_exists('number_of_concurrency', $clientOptions)) {
$numberOfConcurrency = $clientOptions['number_of_concurrency'];
unset($clientOptions['number_of_concurrency']);
}
//create the client
$client = $this->createClient($clientOptions);
$promises = \call_user_func(
function () use ($requests, $generator, $client) {
$sendAsync = function ($request) use ($client) {
$options = $request->getMethod() == 'HEAD'?
array('decode_content' => false) : array();
return $client->sendAsync($request, $options);
};
foreach ($requests as $request) {
yield $sendAsync($request);
}
while (is_callable($generator) && ($request = $generator())) {
yield $sendAsync($request);
}
}
);
$eachPromise = new EachPromise($promises, [
'concurrency' => $numberOfConcurrency,
'fulfilled' => function ($response, $index) use ($expectedStatusCode) {
//the promise is fulfilled, evaluate the response
self::throwIfError(
$response->getStatusCode(),
$response->getReasonPhrase(),
$response->getBody(),
$expectedStatusCode
);
},
'rejected' => function ($reason, $index) {
//Still rejected even if the retry logic has been applied.
//Throwing exception.
throw $reason;
}
]);
return $eachPromise->promise()->wait();
}
/**
* Create the request to be sent.
*
* @param string $method The method of the HTTP request
* @param array $headers The header field of the request
* @param array $queryParams The query parameter of the request
* @param array $postParameters The HTTP POST parameters
* @param string $path URL path
* @param string $body Request body
*
* @return \GuzzleHttp\Psr7\Request
*/
protected function createRequest(
$method,
$headers,
$queryParams,
$postParameters,
$path,
$body = Resources::EMPTY_STRING
) {
// add query parameters into headers
$uri = $this->_psrUri;
if ($path != null) {
$uri = $uri->withPath($path);
}
if ($queryParams != null) {
$queryString = Psr7\build_query($queryParams);
$uri = $uri->withQuery($queryString);
}
// add post parameters into bodys
$actualBody = null;
if (empty($body)) {
if (empty($headers['content-type'])) {
$headers['content-type'] = 'application/x-www-form-urlencoded';
$actualBody = Psr7\build_query($postParameters);
}
} else {
$actualBody = $body;
}
$request = new Request(
$method,
$uri,
$headers,
$actualBody
);
//add content-length to header
$bodySize = $request->getBody()->getSize();
if ($bodySize > 0) {
$request = $request->withHeader('content-length', $bodySize);
}
// Apply filters to the requests
return $this->requestWithFilter($request);
}
/**
* Sends HTTP request with the specified parameters.
*
* @param string $method HTTP method used in the request
* @param array $headers HTTP headers.
* @param array $queryParams URL query parameters.
* @param array $postParameters The HTTP POST parameters.
* @param string $path URL path
* @param int $statusCode Expected status code received in the response
* @param string $body Request body
* @param array $clientOptions Guzzle Client options
*
* @return GuzzleHttp\Psr7\Response
*/
protected function send(
$method,
$headers,
$queryParams,
$postParameters,
$path,
$statusCode,
$body = Resources::EMPTY_STRING,
$clientOptions = []
) {
$request = $this->createRequest(
$method,
$headers,
$queryParams,
$postParameters,
$path,
$body
);
$client = $this->createClient($clientOptions);
try {
$options = $request->getMethod() == 'HEAD'?
array('decode_content' => false) : array();
$response = $client->send($request, $options);
self::throwIfError(
$response->getStatusCode(),
$response->getReasonPhrase(),
$response->getBody(),
$statusCode
);
return $response;
} catch (\GuzzleHttp\Exception\RequestException $e) {
if ($e->hasResponse()) {
$response = $e->getResponse();
self::throwIfError(
$response->getStatusCode(),
$response->getReasonPhrase(),
$response->getBody(),
$statusCode
);
return $response;
} else {
throw $e;
}
}
}
protected function sendContext($context)
{
return $this->send(
$context->getMethod(),
$context->getHeaders(),
$context->getQueryParameters(),
$context->getPostParameters(),
$context->getPath(),
$context->getStatusCodes(),
$context->getBody()
);
}
/**
* Throws ServiceException if the received status code is not expected.
*
* @param string $actual The received status code.
* @param string $reason The reason phrase.
* @param string $message The detailed message (if any).
* @param string $expected The expected status codes.
*
* @return none
*
* @static
*
* @throws ServiceException
*/
public static function throwIfError($actual, $reason, $message, $expected)
{
$expectedStatusCodes = is_array($expected) ? $expected : array($expected);
if (!in_array($actual, $expectedStatusCodes)) {
throw new ServiceException($actual, $reason, $message);
}
}
/**
* Adds optional header to headers if set
*
* @param array $headers The array of request headers.
* @param AccessCondition $accessCondition The access condition object.
*
* @return array
*/
public function addOptionalAccessConditionHeader($headers, $accessCondition)
{
if (!is_null($accessCondition)) {
$header = $accessCondition->getHeader();
if ($header != Resources::EMPTY_STRING) {
$value = $accessCondition->getValue();
if ($value instanceof \DateTime) {
$value = gmdate(
Resources::AZURE_DATE_FORMAT,
$value->getTimestamp()
);
}
$headers[$header] = $value;
}
}
return $headers;
}
/**
* Adds optional header to headers if set
*
* @param array $headers The array of request headers.
* @param AccessCondition $accessCondition The access condition object.
*
* @return array
*/
public function addOptionalSourceAccessConditionHeader(
$headers,
$accessCondition
) {
if (!is_null($accessCondition)) {
$header = $accessCondition->getHeader();
$headerName = null;
if (!empty($header)) {
switch ($header) {
case Resources::IF_MATCH:
$headerName = Resources::X_MS_SOURCE_IF_MATCH;
break;
case Resources::IF_UNMODIFIED_SINCE:
$headerName = Resources::X_MS_SOURCE_IF_UNMODIFIED_SINCE;
break;
case Resources::IF_MODIFIED_SINCE:
$headerName = Resources::X_MS_SOURCE_IF_MODIFIED_SINCE;
break;
case Resources::IF_NONE_MATCH:
$headerName = Resources::X_MS_SOURCE_IF_NONE_MATCH;
break;
default:
throw new \Exception(Resources::INVALID_ACH_MSG);
break;
}
}
$value = $accessCondition->getValue();
if ($value instanceof \DateTime) {
$value = gmdate(
Resources::AZURE_DATE_FORMAT,
$value->getTimestamp()
);
}
$this->addOptionalHeader($headers, $headerName, $value);
}
return $headers;
}
/**
* Adds HTTP POST parameter to the specified
*
* @param array $postParameters An array of HTTP POST parameters.
* @param string $key The key of a HTTP POST parameter.
* @param string $value the value of a HTTP POST parameter.
*
* @return array
*/
public function addPostParameter(
$postParameters,
$key,
$value
) {
Validate::isArray($postParameters, 'postParameters');
$postParameters[$key] = $value;
return $postParameters;
}
/**
* Groups set of values into one value separated with Resources::SEPARATOR
*
* @param array $values array of values to be grouped.
*
* @return string
*/
public static function groupQueryValues($values)
{
Validate::isArray($values, 'values');
$joined = Resources::EMPTY_STRING;
sort($values);
foreach ($values as $value) {
if (!is_null($value) && !empty($value)) {
$joined .= $value . Resources::SEPARATOR;
}
}
return trim($joined, Resources::SEPARATOR);
}
/**
* Adds metadata elements to headers array
*
* @param array $headers HTTP request headers
* @param array $metadata user specified metadata
*
* @return array
*/
protected function addMetadataHeaders($headers, $metadata)
{
$this->validateMetadata($metadata);
$metadata = $this->generateMetadataHeaders($metadata);
$headers = array_merge($headers, $metadata);
return $headers;
}
/**
* Generates metadata headers by prefixing each element with 'x-ms-meta'.
*
* @param array $metadata user defined metadata.
*
* @return array.
*/
public function generateMetadataHeaders($metadata)
{
$metadataHeaders = array();
if (is_array($metadata) && !is_null($metadata)) {
foreach ($metadata as $key => $value) {
$headerName = Resources::X_MS_META_HEADER_PREFIX;
if (strpos($value, "\r") !== false
|| strpos($value, "\n") !== false
) {
throw new \InvalidArgumentException(Resources::INVALID_META_MSG);
}
// Metadata name is case-presrved and case insensitive
$headerName .= $key;
$metadataHeaders[$headerName] = $value;
}
}
return $metadataHeaders;
}
/**
* Gets metadata array by parsing them from given headers.
*
* @param array $headers HTTP headers containing metadata elements.
*
* @return array.
*/
public function getMetadataArray($headers)
{
$metadata = array();
foreach ($headers as $key => $value) {
$isMetadataHeader = Utilities::startsWith(
strtolower($key),
Resources::X_MS_META_HEADER_PREFIX
);
if ($isMetadataHeader) {
// Metadata name is case-presrved and case insensitive
$MetadataName = str_ireplace(
Resources::X_MS_META_HEADER_PREFIX,
Resources::EMPTY_STRING,
$key
);
$metadata[$MetadataName] = $value;
}
}
return $metadata;
}
/**
* Validates the provided metadata array.
*
* @param mix $metadata The metadata array.
*
* @return none
*/
public function validateMetadata($metadata)
{
if (!is_null($metadata)) {
Validate::isArray($metadata, 'metadata');
} else {
$metadata = array();
}
foreach ($metadata as $key => $value) {
Validate::isString($key, 'metadata key');
Validate::isString($value, 'metadata value');
}
}
}