Обновление клиента (apps, 3rdparty, install)
This commit is contained in:
+200
@@ -0,0 +1,200 @@
|
||||
<?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\Middlewares
|
||||
* @author Azure Storage PHP SDK <dmsh@microsoft.com>
|
||||
* @copyright 2017 Microsoft Corporation
|
||||
* @license https://github.com/azure/azure-storage-php/LICENSE
|
||||
* @link https://github.com/azure/azure-storage-php
|
||||
*/
|
||||
|
||||
namespace MicrosoftAzure\Storage\Common\Middlewares;
|
||||
|
||||
use MicrosoftAzure\Storage\Common\Internal\Validate;
|
||||
use MicrosoftAzure\Storage\Common\Internal\Utilities;
|
||||
use MicrosoftAzure\Storage\Common\Internal\Serialization\MessageSerializer;
|
||||
use Psr\Http\Message\RequestInterface;
|
||||
use Psr\Http\Message\ResponseInterface;
|
||||
use GuzzleHttp\Promise\RejectedPromise;
|
||||
|
||||
/**
|
||||
* This class provides the functionality to log the requests/options/responses.
|
||||
* Logging large number of entries without providing a file path may exhaust
|
||||
* the memory.
|
||||
*
|
||||
* The middleware should be pushed into client options if the logging is
|
||||
* intended to persist between different API calls.
|
||||
*
|
||||
* @category Microsoft
|
||||
* @package MicrosoftAzure\Storage\Common\Middlewares
|
||||
* @author Azure Storage PHP SDK <dmsh@microsoft.com>
|
||||
* @copyright 2017 Microsoft Corporation
|
||||
* @license https://github.com/azure/azure-storage-php/LICENSE
|
||||
* @link https://github.com/azure/azure-storage-php
|
||||
*/
|
||||
class HistoryMiddleware extends MiddlewareBase
|
||||
{
|
||||
private $history;
|
||||
private $path;
|
||||
private $count;
|
||||
|
||||
const TITLE_LENGTH = 120;
|
||||
|
||||
/**
|
||||
* Gets the saved paried history.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function getHistory()
|
||||
{
|
||||
return $this->history;
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*
|
||||
* @param string $path the path to save the history. If path is provided,
|
||||
* no data is going to be saved to memory and the
|
||||
* entries are going to be serialized and saved to given
|
||||
* path.
|
||||
*
|
||||
*/
|
||||
public function __construct($path = '')
|
||||
{
|
||||
$this->history = array();
|
||||
$this->path = $path;
|
||||
$this->count = 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add an entry to history
|
||||
*
|
||||
* @param array $entry the entry to be added.
|
||||
*/
|
||||
public function addHistory(array $entry)
|
||||
{
|
||||
if ($this->path !== '') {
|
||||
$this->appendNewEntryToPath($entry);
|
||||
} else {
|
||||
Validate::isTrue(
|
||||
array_key_exists('request', $entry) &&
|
||||
array_key_exists('options', $entry) &&
|
||||
(array_key_exists('response', $entry) ||
|
||||
array_key_exists('reason', $entry)),
|
||||
'Given history entry not in correct format'
|
||||
);
|
||||
$this->history[] = $entry;
|
||||
}
|
||||
++$this->count;
|
||||
}
|
||||
|
||||
/**
|
||||
* Clear the history
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function clearHistory()
|
||||
{
|
||||
$this->history = array();
|
||||
$this->count = 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* This function will be invoked after the request is sent, if
|
||||
* the promise is fulfilled.
|
||||
*
|
||||
* @param RequestInterface $request the request sent.
|
||||
* @param array $options the options that the request sent with.
|
||||
*
|
||||
* @return callable
|
||||
*/
|
||||
protected function onFulfilled(RequestInterface $request, array $options)
|
||||
{
|
||||
$reflection = $this;
|
||||
return function (ResponseInterface $response) use (
|
||||
$reflection,
|
||||
$request,
|
||||
$options
|
||||
) {
|
||||
$reflection->addHistory([
|
||||
'request' => $request,
|
||||
'response' => $response,
|
||||
'options' => $options
|
||||
]);
|
||||
return $response;
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* This function will be executed after the request is sent, if
|
||||
* the promise is rejected.
|
||||
*
|
||||
* @param RequestInterface $request the request sent.
|
||||
* @param array $options the options that the request sent with.
|
||||
*
|
||||
* @return callable
|
||||
*/
|
||||
protected function onRejected(RequestInterface $request, array $options)
|
||||
{
|
||||
$reflection = $this;
|
||||
return function ($reason) use (
|
||||
$reflection,
|
||||
$request,
|
||||
$options
|
||||
) {
|
||||
$reflection->addHistory([
|
||||
'request' => $request,
|
||||
'reason' => $reason,
|
||||
'options' => $options
|
||||
]);
|
||||
return new RejectedPromise($reason);
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Append the new entry to saved file path.
|
||||
*
|
||||
* @param array $entry the entry to be added.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
private function appendNewEntryToPath(array $entry)
|
||||
{
|
||||
$entryNoString = "Entry " . $this->count;
|
||||
$delimiter = str_pad(
|
||||
$entryNoString,
|
||||
self::TITLE_LENGTH,
|
||||
'-',
|
||||
STR_PAD_BOTH
|
||||
) . PHP_EOL;
|
||||
$entryString = $delimiter;
|
||||
$entryString .= sprintf(
|
||||
"Time: %s\n",
|
||||
(new \DateTime("now", new \DateTimeZone('UTC')))->format('Y-m-d H:i:s')
|
||||
);
|
||||
$entryString .= MessageSerializer::objectSerialize($entry['request']);
|
||||
if (array_key_exists('reason', $entry)) {
|
||||
$entryString .= MessageSerializer::objectSerialize($entry['reason']);
|
||||
} elseif (array_key_exists('response', $entry)) {
|
||||
$entryString .= MessageSerializer::objectSerialize($entry['response']);
|
||||
}
|
||||
|
||||
$entryString .= $delimiter;
|
||||
|
||||
Utilities::appendToFile($this->path, $entryString);
|
||||
}
|
||||
}
|
||||
+71
@@ -0,0 +1,71 @@
|
||||
<?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\Middlewares
|
||||
* @author Azure Storage PHP SDK <dmsh@microsoft.com>
|
||||
* @copyright 2017 Microsoft Corporation
|
||||
* @license https://github.com/azure/azure-storage-php/LICENSE
|
||||
* @link https://github.com/azure/azure-storage-php
|
||||
*/
|
||||
|
||||
namespace MicrosoftAzure\Storage\Common\Middlewares;
|
||||
|
||||
/**
|
||||
* IMiddleware is called before sending the request and after receiving the
|
||||
* response.
|
||||
*
|
||||
* @category Microsoft
|
||||
* @package MicrosoftAzure\Storage\Common\Middlewares
|
||||
* @author Azure Storage PHP SDK <dmsh@microsoft.com>
|
||||
* @copyright 2017 Microsoft Corporation
|
||||
* @license https://github.com/azure/azure-storage-php/LICENSE
|
||||
* @link https://github.com/azure/azure-storage-php
|
||||
*/
|
||||
|
||||
interface IMiddleware
|
||||
{
|
||||
/**
|
||||
* This function will return a callable with $request and $options as
|
||||
* its parameters and returns a promise. The callable can modify the
|
||||
* request, fulfilled response or rejected reason when invoked with certain
|
||||
* conditions. Sample middleware implementation:
|
||||
*
|
||||
* ```
|
||||
* return function (
|
||||
* RequestInterface $request,
|
||||
* array $options
|
||||
* ) use ($handler) {
|
||||
* //do something prior to sending the request.
|
||||
* $promise = $handler($request, $options);
|
||||
* return $promise->then(
|
||||
* function (ResponseInterface $response) use ($request, $options) {
|
||||
* //do something
|
||||
* return $response;
|
||||
* },
|
||||
* function ($reason) use ($request, $options) {
|
||||
* //do something
|
||||
* return new GuzzleHttp\Promise\RejectedPromise($reason);
|
||||
* }
|
||||
* );
|
||||
* };
|
||||
* ```
|
||||
*
|
||||
* @param callable $handler The next handler.
|
||||
* @return callable
|
||||
*/
|
||||
public function __invoke(callable $handler);
|
||||
}
|
||||
+115
@@ -0,0 +1,115 @@
|
||||
<?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\Middlewares
|
||||
* @author Azure Storage PHP SDK <dmsh@microsoft.com>
|
||||
* @copyright 2017 Microsoft Corporation
|
||||
* @license https://github.com/azure/azure-storage-php/LICENSE
|
||||
* @link https://github.com/azure/azure-storage-php
|
||||
*/
|
||||
|
||||
namespace MicrosoftAzure\Storage\Common\Middlewares;
|
||||
|
||||
use Psr\Http\Message\RequestInterface;
|
||||
use Psr\Http\Message\ResponseInterface;
|
||||
use GuzzleHttp\Promise\RejectedPromise;
|
||||
|
||||
/**
|
||||
* This class provides the base structure of middleware that can be used for
|
||||
* doing customized behavior including modifying the request, response or
|
||||
* other behaviors like logging, retrying and debugging.
|
||||
*
|
||||
* @category Microsoft
|
||||
* @package MicrosoftAzure\Storage\Common\Middlewares
|
||||
* @author Azure Storage PHP SDK <dmsh@microsoft.com>
|
||||
* @copyright 2017 Microsoft Corporation
|
||||
* @license https://github.com/azure/azure-storage-php/LICENSE
|
||||
* @link https://github.com/azure/azure-storage-php
|
||||
*/
|
||||
class MiddlewareBase implements IMiddleware
|
||||
{
|
||||
|
||||
/**
|
||||
* Middleware augments the functionality of handlers by invoking them
|
||||
* in the process of generating responses. And it returns a function
|
||||
* that accepts the next handler to invoke. Refer to
|
||||
* http://docs.guzzlephp.org/en/latest/handlers-and-middleware.html#middleware
|
||||
* for more detailed information.
|
||||
*
|
||||
* @param callable The handler function.
|
||||
*
|
||||
* @return callable The function that accepts the next handler to invoke.
|
||||
*/
|
||||
public function __invoke(callable $handler)
|
||||
{
|
||||
$reflection = $this;
|
||||
return function ($request, $options) use ($handler, $reflection) {
|
||||
$request = $reflection->onRequest($request);
|
||||
return $handler($request, $options)->then(
|
||||
$reflection->onFulfilled($request, $options),
|
||||
$reflection->onRejected($request, $options)
|
||||
);
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* This function will be executed before the request is sent.
|
||||
*
|
||||
* @param RequestInterface $request the request before altered.
|
||||
*
|
||||
* @return RequestInterface the request after altered.
|
||||
*/
|
||||
protected function onRequest(RequestInterface $request)
|
||||
{
|
||||
//do nothing
|
||||
return $request;
|
||||
}
|
||||
|
||||
/**
|
||||
* This function will be invoked after the request is sent, if
|
||||
* the promise is fulfilled.
|
||||
*
|
||||
* @param RequestInterface $request the request sent.
|
||||
* @param array $options the options that the request sent with.
|
||||
*
|
||||
* @return callable
|
||||
*/
|
||||
protected function onFulfilled(RequestInterface $request, array $options)
|
||||
{
|
||||
return function (ResponseInterface $response) {
|
||||
//do nothing
|
||||
return $response;
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* This function will be executed after the request is sent, if
|
||||
* the promise is rejected.
|
||||
*
|
||||
* @param RequestInterface $request the request sent.
|
||||
* @param array $options the options that the request sent with.
|
||||
*
|
||||
* @return callable
|
||||
*/
|
||||
protected function onRejected(RequestInterface $request, array $options)
|
||||
{
|
||||
return function ($reason) {
|
||||
//do nothing
|
||||
return new RejectedPromise($reason);
|
||||
};
|
||||
}
|
||||
}
|
||||
+70
@@ -0,0 +1,70 @@
|
||||
<?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\Middlewares
|
||||
* @author Azure Storage PHP SDK <dmsh@microsoft.com>
|
||||
* @copyright 2017 Microsoft Corporation
|
||||
* @license https://github.com/azure/azure-storage-php/LICENSE
|
||||
* @link https://github.com/azure/azure-storage-php
|
||||
*/
|
||||
|
||||
namespace MicrosoftAzure\Storage\Common\Middlewares;
|
||||
|
||||
/**
|
||||
* This class provides the stack that handles the logic of applying each
|
||||
* middlewares to the request or the response.
|
||||
*
|
||||
* @category Microsoft
|
||||
* @package MicrosoftAzure\Storage\Common\Middlewares
|
||||
* @author Azure Storage PHP SDK <dmsh@microsoft.com>
|
||||
* @copyright 2017 Microsoft Corporation
|
||||
* @license https://github.com/azure/azure-storage-php/LICENSE
|
||||
* @link https://github.com/azure/azure-storage-php
|
||||
*/
|
||||
class MiddlewareStack
|
||||
{
|
||||
private $middlewares = array();
|
||||
|
||||
/**
|
||||
* Push the given middleware into the middleware stack.
|
||||
*
|
||||
* @param IMiddleware|callable $middleware The middleware to be pushed.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function push($middleware)
|
||||
{
|
||||
array_unshift($this->middlewares, $middleware);
|
||||
}
|
||||
|
||||
/**
|
||||
* Apply the middlewares to the handler.
|
||||
*
|
||||
* @param callable $handler the handler to which the middleware applies.
|
||||
*
|
||||
* @return callable
|
||||
*/
|
||||
public function apply(callable $handler)
|
||||
{
|
||||
$result = $handler;
|
||||
foreach ($this->middlewares as $middleware) {
|
||||
$result = $middleware($result);
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
}
|
||||
+181
@@ -0,0 +1,181 @@
|
||||
<?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\Middlewares
|
||||
* @author Azure Storage PHP SDK <dmsh@microsoft.com>
|
||||
* @copyright 2017 Microsoft Corporation
|
||||
* @license https://github.com/azure/azure-storage-php/LICENSE
|
||||
* @link https://github.com/azure/azure-storage-php
|
||||
*/
|
||||
|
||||
namespace MicrosoftAzure\Storage\Common\Middlewares;
|
||||
|
||||
use MicrosoftAzure\Storage\Common\LocationMode;
|
||||
use MicrosoftAzure\Storage\Common\Internal\Resources;
|
||||
use MicrosoftAzure\Storage\Common\Internal\Utilities;
|
||||
use Psr\Http\Message\RequestInterface;
|
||||
use Psr\Http\Message\ResponseInterface;
|
||||
use GuzzleHttp\Psr7\Uri;
|
||||
use GuzzleHttp\Promise\RejectedPromise;
|
||||
|
||||
/**
|
||||
* This class provides the functionality of a middleware that handles all the
|
||||
* retry logic for the request.
|
||||
*
|
||||
* @category Microsoft
|
||||
* @package MicrosoftAzure\Storage\Common\Middlewares
|
||||
* @author Azure Storage PHP SDK <dmsh@microsoft.com>
|
||||
* @copyright 2017 Microsoft Corporation
|
||||
* @license https://github.com/azure/azure-storage-php/LICENSE
|
||||
* @link https://github.com/azure/azure-storage-php
|
||||
*/
|
||||
class RetryMiddleware extends MiddlewareBase
|
||||
{
|
||||
private $intervalCalculator;
|
||||
private $decider;
|
||||
|
||||
public function __construct(
|
||||
callable $intervalCalculator,
|
||||
callable $decider
|
||||
) {
|
||||
$this->intervalCalculator = $intervalCalculator;
|
||||
$this->decider = $decider;
|
||||
}
|
||||
|
||||
/**
|
||||
* This function will be invoked after the request is sent, if
|
||||
* the promise is fulfilled.
|
||||
*
|
||||
* @param RequestInterface $request the request sent.
|
||||
* @param array $options the options that the request sent with.
|
||||
*
|
||||
* @return callable
|
||||
*/
|
||||
protected function onFulfilled(RequestInterface $request, array $options)
|
||||
{
|
||||
return function (ResponseInterface $response) use ($request, $options) {
|
||||
$isSecondary = Utilities::requestSentToSecondary($request, $options);
|
||||
if (!isset($options['retries'])) {
|
||||
$options['retries'] = 0;
|
||||
}
|
||||
if (call_user_func(
|
||||
$this->decider,
|
||||
$options['retries'],
|
||||
$request,
|
||||
$response,
|
||||
null,
|
||||
$isSecondary
|
||||
)) {
|
||||
return $this->retry($request, $options, $response);
|
||||
}
|
||||
//Add the header that indicates the endpoint to be used if
|
||||
//continuation token is used for subsequent request.
|
||||
if ($isSecondary) {
|
||||
$response = $response->withHeader(
|
||||
Resources::X_MS_CONTINUATION_LOCATION_MODE,
|
||||
LocationMode::SECONDARY_ONLY
|
||||
);
|
||||
} else {
|
||||
$response = $response->withHeader(
|
||||
Resources::X_MS_CONTINUATION_LOCATION_MODE,
|
||||
LocationMode::PRIMARY_ONLY
|
||||
);
|
||||
}
|
||||
return $response;
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* This function will be executed after the request is sent, if
|
||||
* the promise is rejected.
|
||||
*
|
||||
* @param RequestInterface $request the request sent.
|
||||
* @param array $options the options that the request sent with.
|
||||
*
|
||||
* @return callable
|
||||
*/
|
||||
protected function onRejected(RequestInterface $request, array $options)
|
||||
{
|
||||
return function ($reason) use ($request, $options) {
|
||||
$isSecondary = Utilities::requestSentToSecondary($request, $options);
|
||||
if (!isset($options['retries'])) {
|
||||
$options['retries'] = 0;
|
||||
}
|
||||
|
||||
if (call_user_func(
|
||||
$this->decider,
|
||||
$options['retries'],
|
||||
$request,
|
||||
null,
|
||||
$reason,
|
||||
$isSecondary
|
||||
)) {
|
||||
return $this->retry($request, $options);
|
||||
}
|
||||
return new RejectedPromise($reason);
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* This function does the real retry job.
|
||||
*
|
||||
* @param RequestInterface $request the request sent.
|
||||
* @param array $options the options that the request sent with.
|
||||
* @param ResponseInterface $response the response of the request
|
||||
*
|
||||
* @return callable
|
||||
*/
|
||||
private function retry(
|
||||
RequestInterface $request,
|
||||
array $options,
|
||||
ResponseInterface $response = null
|
||||
) {
|
||||
$options['delay'] = call_user_func(
|
||||
$this->intervalCalculator,
|
||||
++$options['retries']
|
||||
);
|
||||
|
||||
//Change the request URI according to the location mode.
|
||||
if (array_key_exists(Resources::ROS_LOCATION_MODE, $options)) {
|
||||
$locationMode = $options[Resources::ROS_LOCATION_MODE];
|
||||
//If have RA-GRS enabled for the request, switch between
|
||||
//primary and secondary.
|
||||
if ($locationMode == LocationMode::PRIMARY_THEN_SECONDARY ||
|
||||
$locationMode == LocationMode::SECONDARY_THEN_PRIMARY) {
|
||||
$primaryUri = $options[Resources::ROS_PRIMARY_URI];
|
||||
$secondaryUri = $options[Resources::ROS_SECONDARY_URI];
|
||||
|
||||
$target = $request->getRequestTarget();
|
||||
if (Utilities::startsWith($target, '/')) {
|
||||
$target = substr($target, 1);
|
||||
$primaryUri = new Uri($primaryUri . $target);
|
||||
$secondaryUri = new Uri($secondaryUri . $target);
|
||||
}
|
||||
|
||||
//substitute the uri.
|
||||
if ((string)$request->getUri() == (string)$primaryUri) {
|
||||
$request = $request->withUri($secondaryUri);
|
||||
} elseif ((string)$request->getUri() == (string)$secondaryUri) {
|
||||
$request = $request->withUri($primaryUri);
|
||||
}
|
||||
}
|
||||
}
|
||||
$handler = $options[Resources::ROS_HANDLER];
|
||||
|
||||
return \call_user_func($handler, $request, $options);
|
||||
}
|
||||
}
|
||||
+271
@@ -0,0 +1,271 @@
|
||||
<?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\Middlewares
|
||||
* @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\Middlewares;
|
||||
|
||||
use MicrosoftAzure\Storage\Common\Internal\Resources;
|
||||
use MicrosoftAzure\Storage\Common\Internal\Validate;
|
||||
use GuzzleHttp\Exception\ConnectException;
|
||||
use GuzzleHttp\Exception\RequestException;
|
||||
|
||||
/**
|
||||
* This class provides static functions that creates retry handlers for Guzzle
|
||||
* HTTP clients to handle retry policy.
|
||||
*
|
||||
* @category Microsoft
|
||||
* @package MicrosoftAzure\Storage\Common\Middlewares
|
||||
* @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
|
||||
*/
|
||||
class RetryMiddlewareFactory
|
||||
{
|
||||
//The interval will be increased linearly, the nth retry will have a
|
||||
//wait time equal to n * interval.
|
||||
const LINEAR_INTERVAL_ACCUMULATION = 'Linear';
|
||||
//The interval will be increased exponentially, the nth retry will have a
|
||||
//wait time equal to pow(2, n) * interval.
|
||||
const EXPONENTIAL_INTERVAL_ACCUMULATION = 'Exponential';
|
||||
//This is for the general type of logic that handles retry.
|
||||
const GENERAL_RETRY_TYPE = 'General';
|
||||
//This is for the append blob retry only.
|
||||
const APPEND_BLOB_RETRY_TYPE = 'Append Blob Retry';
|
||||
|
||||
/**
|
||||
* Create the retry handler for the Guzzle client, according to the given
|
||||
* attributes.
|
||||
*
|
||||
* @param string $type The type that controls the logic of
|
||||
* the decider of the retry handler.
|
||||
* Possible value can be
|
||||
* self::GENERAL_RETRY_TYPE or
|
||||
* self::APPEND_BLOB_RETRY_TYPE
|
||||
* @param int $numberOfRetries The maximum number of retries.
|
||||
* @param int $interval The minimum interval between each retry
|
||||
* @param string $accumulationMethod If the interval increases linearly or
|
||||
* exponentially.
|
||||
* Possible value can be
|
||||
* self::LINEAR_INTERVAL_ACCUMULATION or
|
||||
* self::EXPONENTIAL_INTERVAL_ACCUMULATION
|
||||
* @param bool $retryConnect Whether to retry on connection failures.
|
||||
* @return RetryMiddleware A RetryMiddleware object that contains
|
||||
* the logic of how the request should be
|
||||
* handled after a response.
|
||||
*/
|
||||
public static function create(
|
||||
$type = self::GENERAL_RETRY_TYPE,
|
||||
$numberOfRetries = Resources::DEFAULT_NUMBER_OF_RETRIES,
|
||||
$interval = Resources::DEFAULT_RETRY_INTERVAL,
|
||||
$accumulationMethod = self::LINEAR_INTERVAL_ACCUMULATION,
|
||||
$retryConnect = false
|
||||
) {
|
||||
//Validate the input parameters
|
||||
//type
|
||||
Validate::isTrue(
|
||||
$type == self::GENERAL_RETRY_TYPE ||
|
||||
$type == self::APPEND_BLOB_RETRY_TYPE,
|
||||
sprintf(
|
||||
Resources::INVALID_PARAM_GENERAL,
|
||||
'type'
|
||||
)
|
||||
);
|
||||
//numberOfRetries
|
||||
Validate::isTrue(
|
||||
$numberOfRetries > 0,
|
||||
sprintf(
|
||||
Resources::INVALID_NEGATIVE_PARAM,
|
||||
'numberOfRetries'
|
||||
)
|
||||
);
|
||||
//interval
|
||||
Validate::isTrue(
|
||||
$interval > 0,
|
||||
sprintf(
|
||||
Resources::INVALID_NEGATIVE_PARAM,
|
||||
'interval'
|
||||
)
|
||||
);
|
||||
//accumulationMethod
|
||||
Validate::isTrue(
|
||||
$accumulationMethod == self::LINEAR_INTERVAL_ACCUMULATION ||
|
||||
$accumulationMethod == self::EXPONENTIAL_INTERVAL_ACCUMULATION,
|
||||
sprintf(
|
||||
Resources::INVALID_PARAM_GENERAL,
|
||||
'accumulationMethod'
|
||||
)
|
||||
);
|
||||
//retryConnect
|
||||
Validate::isBoolean($retryConnect);
|
||||
|
||||
//Get the interval calculator according to the type of the
|
||||
//accumulation method.
|
||||
$intervalCalculator =
|
||||
$accumulationMethod == self::LINEAR_INTERVAL_ACCUMULATION ?
|
||||
static::createLinearDelayCalculator($interval) :
|
||||
static::createExponentialDelayCalculator($interval);
|
||||
|
||||
//Get the retry decider according to the type of the retry and
|
||||
//the number of retries.
|
||||
$retryDecider = static::createRetryDecider($type, $numberOfRetries, $retryConnect);
|
||||
|
||||
//construct the retry middle ware.
|
||||
return new RetryMiddleware($intervalCalculator, $retryDecider);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create the retry decider for the retry handler. It will return a callable
|
||||
* that accepts the number of retries, the request, the response and the
|
||||
* exception, and return the decision for a retry.
|
||||
*
|
||||
* @param string $type The type of the retry handler.
|
||||
* @param int $maxRetries The maximum number of retries to be done.
|
||||
* @param bool $retryConnect Whether to retry on connection failures.
|
||||
*
|
||||
* @return callable The callable that will return if the request should
|
||||
* be retried.
|
||||
*/
|
||||
protected static function createRetryDecider($type, $maxRetries, $retryConnect)
|
||||
{
|
||||
return function (
|
||||
$retries,
|
||||
$request,
|
||||
$response = null,
|
||||
$exception = null,
|
||||
$isSecondary = false
|
||||
) use (
|
||||
$type,
|
||||
$maxRetries,
|
||||
$retryConnect
|
||||
) {
|
||||
//Exceeds the retry limit. No retry.
|
||||
if ($retries >= $maxRetries) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!$response) {
|
||||
if (!$exception || !($exception instanceof RequestException)) {
|
||||
return false;
|
||||
} elseif ($exception instanceof ConnectException) {
|
||||
return $retryConnect;
|
||||
} else {
|
||||
$response = $exception->getResponse();
|
||||
if (!$response) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ($type == self::GENERAL_RETRY_TYPE) {
|
||||
return static::generalRetryDecider(
|
||||
$response->getStatusCode(),
|
||||
$isSecondary
|
||||
);
|
||||
} else {
|
||||
return static::appendBlobRetryDecider(
|
||||
$response->getStatusCode(),
|
||||
$isSecondary
|
||||
);
|
||||
}
|
||||
|
||||
return true;
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Decide if the given status code indicate the request should be retried.
|
||||
*
|
||||
* @param int $statusCode Status code of the previous request.
|
||||
* @param bool $isSecondary Whether the request is sent to secondary endpoint.
|
||||
*
|
||||
* @return bool true if the request should be retried.
|
||||
*/
|
||||
protected static function generalRetryDecider($statusCode, $isSecondary)
|
||||
{
|
||||
$retry = false;
|
||||
if ($statusCode == 408) {
|
||||
$retry = true;
|
||||
} elseif ($statusCode >= 500) {
|
||||
if ($statusCode != 501 && $statusCode != 505) {
|
||||
$retry = true;
|
||||
}
|
||||
} elseif ($isSecondary && $statusCode == 404) {
|
||||
$retry = true;
|
||||
}
|
||||
return $retry;
|
||||
}
|
||||
|
||||
/**
|
||||
* Decide if the given status code indicate the request should be retried.
|
||||
* This is for append blob.
|
||||
*
|
||||
* @param int $statusCode Status code of the previous request.
|
||||
* @param bool $isSecondary Whether the request is sent to secondary endpoint.
|
||||
*
|
||||
* @return bool true if the request should be retried.
|
||||
*/
|
||||
protected static function appendBlobRetryDecider($statusCode, $isSecondary)
|
||||
{
|
||||
//The retry logic is different for append blob.
|
||||
//First it will need to record the former status code if it is
|
||||
//server error. Then if the following request is 412 then it
|
||||
//needs to be retried. Currently this is not implemented so will
|
||||
//only adapt to the general retry decider.
|
||||
//TODO: add logic for append blob's retry when implemented.
|
||||
$retry = static::generalRetryDecider($statusCode, $isSecondary);
|
||||
return $retry;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create the delay calculator that increases the interval linearly
|
||||
* according to the number of retries.
|
||||
*
|
||||
* @param int $interval the minimum interval of the retry.
|
||||
*
|
||||
* @return callable a calculator that will return the interval
|
||||
* according to the number of retries.
|
||||
*/
|
||||
protected static function createLinearDelayCalculator($interval)
|
||||
{
|
||||
return function ($retries) use ($interval) {
|
||||
return $retries * $interval;
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Create the delay calculator that increases the interval exponentially
|
||||
* according to the number of retries.
|
||||
*
|
||||
* @param int $interval the minimum interval of the retry.
|
||||
*
|
||||
* @return callable a calculator that will return the interval
|
||||
* according to the number of retries.
|
||||
*/
|
||||
protected static function createExponentialDelayCalculator($interval)
|
||||
{
|
||||
return function ($retries) use ($interval) {
|
||||
return $interval * ((int)\pow(2, $retries));
|
||||
};
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user