24
vendor/php-http/client-common/.php_cs.dist
vendored
Normal file
24
vendor/php-http/client-common/.php_cs.dist
vendored
Normal file
@@ -0,0 +1,24 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* Configuration for fabpot/php-cs-fixer
|
||||
*
|
||||
* @link https://github.com/FriendsOfPHP/PHP-CS-Fixer
|
||||
*/
|
||||
|
||||
$finder = PhpCsFixer\Finder::create()
|
||||
->in('src')
|
||||
->in('spec')
|
||||
;
|
||||
return PhpCsFixer\Config::create()
|
||||
->setRules([
|
||||
'@PSR2' => true,
|
||||
'@Symfony' => true,
|
||||
'array_syntax' => [
|
||||
'syntax' => 'short',
|
||||
],
|
||||
'no_empty_phpdoc' => true,
|
||||
'phpdoc_to_comment' => false,
|
||||
'single_line_throw' => false,
|
||||
])
|
||||
->setFinder($finder);
|
||||
247
vendor/php-http/client-common/CHANGELOG.md
vendored
Normal file
247
vendor/php-http/client-common/CHANGELOG.md
vendored
Normal file
@@ -0,0 +1,247 @@
|
||||
# Change Log
|
||||
|
||||
## 2.3.0 - 2020-07-21
|
||||
|
||||
### Fixed
|
||||
|
||||
- HttpMethodsClient with PSR RequestFactory
|
||||
- Bug in the cookie plugin with empty cookies
|
||||
- Bug when parsing null-valued date headers
|
||||
|
||||
### Changed
|
||||
|
||||
- Deprecation when constructing a HttpMethodsClient with PSR RequestFactory but without a StreamFactory
|
||||
|
||||
## 2.2.1 - 2020-07-13
|
||||
|
||||
### Fixed
|
||||
|
||||
- Support for PHP 8
|
||||
- Plugin callable phpdoc
|
||||
|
||||
## 2.2.0 - 2020-07-02
|
||||
|
||||
### Added
|
||||
|
||||
- Plugin client builder for making a `PluginClient`
|
||||
- Support for the PSR-17 request factory in `HttpMethodsClient`
|
||||
|
||||
### Changed
|
||||
|
||||
- Restored support for `symfony/options-resolver: ^2.6`
|
||||
- Consistent implementation of union type checking
|
||||
|
||||
### Fixed
|
||||
|
||||
- Memory leak when using the `PluginClient` with plugins
|
||||
|
||||
## 2.1.0 - 2019-11-18
|
||||
|
||||
### Added
|
||||
|
||||
- Support Symfony 5
|
||||
|
||||
## 2.0.0 - 2019-02-03
|
||||
|
||||
### Changed
|
||||
|
||||
- HttpClientRouter now throws a HttpClientNoMatchException instead of a RequestException if it can not find a client for the request.
|
||||
- RetryPlugin will only retry exceptions when there is no response, or a response in the 5xx HTTP code range.
|
||||
- RetryPlugin also retries when no exception is thrown if the responses has HTTP code in the 5xx range.
|
||||
The callbacks for exception handling have been renamed and callbacks for response handling have been added.
|
||||
- Abstract method `HttpClientPool::chooseHttpClient()` has now an explicit return type (`Http\Client\Common\HttpClientPoolItem`)
|
||||
- Interface method `Plugin::handleRequest(...)` has now an explicit return type (`Http\Promise\Promise`)
|
||||
- Made classes final that are not intended to be extended.
|
||||
- Added interfaces for BatchClient, HttpClientRouter and HttpMethodsClient.
|
||||
(These interfaces use the `Interface` suffix to avoid name collisions.)
|
||||
- Added an interface for HttpClientPool and moved the abstract class to the HttpClientPool sub namespace.
|
||||
- AddPathPlugin: Do not add the prefix if the URL already has the same prefix.
|
||||
- All exceptions in `Http\Client\Common\Exception` are final.
|
||||
|
||||
### Removed
|
||||
|
||||
- Deprecated option `debug_plugins` has been removed from `PluginClient`
|
||||
- Deprecated options `decider` and `delay` have been removed from `RetryPlugin`, use `exception_decider` and `exception_delay` instead.
|
||||
|
||||
## 1.9.1 - 2019-02-02
|
||||
|
||||
### Added
|
||||
|
||||
- Updated type hints in doc blocks.
|
||||
|
||||
## 1.9.0 - 2019-01-03
|
||||
|
||||
### Added
|
||||
|
||||
- Support for PSR-18 clients
|
||||
- Added traits `VersionBridgePlugin` and `VersionBridgeClient` to help plugins and clients to support both
|
||||
1.x and 2.x version of `php-http/client-common` and `php-http/httplug`.
|
||||
|
||||
### Changed
|
||||
|
||||
- RetryPlugin: Renamed the configuration options for the exception retry callback from `decider` to `exception_decider`
|
||||
and `delay` to `exception_delay`. The old names still work but are deprecated.
|
||||
|
||||
## 1.8.2 - 2018-12-14
|
||||
|
||||
### Changed
|
||||
|
||||
- When multiple cookies exist, a single header with all cookies is sent as per RFC 6265 Section 5.4
|
||||
- AddPathPlugin will now trim of ending slashes in paths
|
||||
|
||||
## 1.8.1 - 2018-10-09
|
||||
|
||||
### Fixed
|
||||
|
||||
- Reverted change to RetryPlugin so it again waits when retrying to avoid "can only throw objects" error.
|
||||
|
||||
## 1.8.0 - 2018-09-21
|
||||
|
||||
### Added
|
||||
|
||||
- Add an option on ErrorPlugin to only throw exception on response with 5XX status code.
|
||||
|
||||
### Changed
|
||||
|
||||
- AddPathPlugin no longer add prefix multiple times if a request is restarted - it now only adds the prefix if that request chain has not yet passed through the AddPathPlugin
|
||||
- RetryPlugin no longer wait for retried requests and use a deferred promise instead
|
||||
|
||||
### Fixed
|
||||
|
||||
- Decoder plugin will now remove header when there is no more encoding, instead of setting to an empty array
|
||||
|
||||
## 1.7.0 - 2017-11-30
|
||||
|
||||
### Added
|
||||
|
||||
- Symfony 4 support
|
||||
|
||||
### Changed
|
||||
|
||||
- Strict comparison in DecoderPlugin
|
||||
|
||||
## 1.6.0 - 2017-10-16
|
||||
|
||||
### Added
|
||||
|
||||
- Add HttpClientPool client to leverage load balancing and fallback mechanism [see the documentation](http://docs.php-http.org/en/latest/components/client-common.html) for more details.
|
||||
- `PluginClientFactory` to create `PluginClient` instances.
|
||||
- Added new option 'delay' for `RetryPlugin`.
|
||||
- Added new option 'decider' for `RetryPlugin`.
|
||||
- Supports more cookie date formats in the Cookie Plugin
|
||||
|
||||
### Changed
|
||||
|
||||
- The `RetryPlugin` does now wait between retries. To disable/change this feature you must write something like:
|
||||
|
||||
```php
|
||||
$plugin = new RetryPlugin(['delay' => function(RequestInterface $request, Exception $e, $retries) {
|
||||
return 0;
|
||||
});
|
||||
```
|
||||
|
||||
### Deprecated
|
||||
|
||||
- The `debug_plugins` option for `PluginClient` is deprecated and will be removed in 2.0. Use the decorator design pattern instead like in [ProfilePlugin](https://github.com/php-http/HttplugBundle/blob/de33f9c14252f22093a5ec7d84f17535ab31a384/Collector/ProfilePlugin.php).
|
||||
|
||||
## 1.5.0 - 2017-03-30
|
||||
|
||||
### Added
|
||||
|
||||
- `QueryDefaultsPlugin` to add default query parameters.
|
||||
|
||||
## 1.4.2 - 2017-03-18
|
||||
|
||||
### Deprecated
|
||||
|
||||
- `DecoderPlugin` does not longer claim to support `compress` content encoding
|
||||
|
||||
### Fixed
|
||||
|
||||
- `CookiePlugin` allows main domain cookies to be sent/stored for subdomains
|
||||
- `DecoderPlugin` uses the right `FilteredStream` to handle `deflate` content encoding
|
||||
|
||||
|
||||
## 1.4.1 - 2017-02-20
|
||||
|
||||
### Fixed
|
||||
|
||||
- Cast return value of `StreamInterface::getSize` to string in `ContentLengthPlugin`
|
||||
|
||||
|
||||
## 1.4.0 - 2016-11-04
|
||||
|
||||
### Added
|
||||
|
||||
- Add Path plugin
|
||||
- Base URI plugin that combines Add Host and Add Path plugins
|
||||
|
||||
|
||||
## 1.3.0 - 2016-10-16
|
||||
|
||||
### Changed
|
||||
|
||||
- Fix Emulated Trait to use Http based promise which respect the HttpAsyncClient interface
|
||||
- Require Httplug 1.1 where we use HTTP specific promises.
|
||||
- RedirectPlugin: use the full URL instead of the URI to properly keep track of redirects
|
||||
- Add AddPathPlugin for API URLs with base path
|
||||
- Add BaseUriPlugin that combines AddHostPlugin and AddPathPlugin
|
||||
|
||||
|
||||
## 1.2.1 - 2016-07-26
|
||||
|
||||
### Changed
|
||||
|
||||
- AddHostPlugin also sets the port if specified
|
||||
|
||||
|
||||
## 1.2.0 - 2016-07-14
|
||||
|
||||
### Added
|
||||
|
||||
- Suggest separate plugins in composer.json
|
||||
- Introduced `debug_plugins` option for `PluginClient`
|
||||
|
||||
|
||||
## 1.1.0 - 2016-05-04
|
||||
|
||||
### Added
|
||||
|
||||
- Add a flexible http client providing both contract, and only emulating what's necessary
|
||||
- HTTP Client Router: route requests to underlying clients
|
||||
- Plugin client and core plugins moved here from `php-http/plugins`
|
||||
|
||||
### Deprecated
|
||||
|
||||
- Extending client classes, they will be made final in version 2.0
|
||||
|
||||
|
||||
## 1.0.0 - 2016-01-27
|
||||
|
||||
### Changed
|
||||
|
||||
- Remove useless interface in BatchException
|
||||
|
||||
|
||||
## 0.2.0 - 2016-01-12
|
||||
|
||||
### Changed
|
||||
|
||||
- Updated package files
|
||||
- Updated HTTPlug to RC1
|
||||
|
||||
|
||||
## 0.1.1 - 2015-12-26
|
||||
|
||||
### Added
|
||||
|
||||
- Emulated clients
|
||||
|
||||
|
||||
## 0.1.0 - 2015-12-25
|
||||
|
||||
### Added
|
||||
|
||||
- Batch client from utils
|
||||
- Methods client from utils
|
||||
- Emulators and decorators from client-tools
|
||||
19
vendor/php-http/client-common/LICENSE
vendored
Normal file
19
vendor/php-http/client-common/LICENSE
vendored
Normal file
@@ -0,0 +1,19 @@
|
||||
Copyright (c) 2015-2016 PHP HTTP Team <team@php-http.org>
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is furnished
|
||||
to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
||||
55
vendor/php-http/client-common/README.md
vendored
Normal file
55
vendor/php-http/client-common/README.md
vendored
Normal file
@@ -0,0 +1,55 @@
|
||||
# HTTP Client Common
|
||||
|
||||
[](https://github.com/php-http/client-common/releases)
|
||||
[](LICENSE)
|
||||
[](https://travis-ci.org/php-http/client-common)
|
||||
[](https://scrutinizer-ci.com/g/php-http/client-common)
|
||||
[](https://scrutinizer-ci.com/g/php-http/client-common)
|
||||
[](https://packagist.org/packages/php-http/client-common)
|
||||
|
||||
**Common HTTP Client implementations and tools for HTTPlug.**
|
||||
|
||||
|
||||
## Install
|
||||
|
||||
Via Composer
|
||||
|
||||
``` bash
|
||||
$ composer require php-http/client-common
|
||||
```
|
||||
|
||||
|
||||
## Usage
|
||||
|
||||
This package provides common tools for HTTP Clients:
|
||||
|
||||
- BatchClient to handle sending requests in parallel
|
||||
- A convenience client with HTTP method names as class methods
|
||||
- Emulator, decorator layers for sync/async clients
|
||||
|
||||
|
||||
## Documentation
|
||||
|
||||
Please see the [official documentation](http://docs.php-http.org/en/latest/components/client-common.html).
|
||||
|
||||
|
||||
## Testing
|
||||
|
||||
``` bash
|
||||
$ composer test
|
||||
```
|
||||
|
||||
|
||||
## Contributing
|
||||
|
||||
Please see our [contributing guide](http://docs.php-http.org/en/latest/development/contributing.html).
|
||||
|
||||
|
||||
## Security
|
||||
|
||||
If you discover any security related issues, please contact us at [security@php-http.org](mailto:security@php-http.org).
|
||||
|
||||
|
||||
## License
|
||||
|
||||
The MIT License (MIT). Please see [License File](LICENSE) for more information.
|
||||
67
vendor/php-http/client-common/composer.json
vendored
Normal file
67
vendor/php-http/client-common/composer.json
vendored
Normal file
@@ -0,0 +1,67 @@
|
||||
{
|
||||
"name": "php-http/client-common",
|
||||
"description": "Common HTTP Client implementations and tools for HTTPlug",
|
||||
"license": "MIT",
|
||||
"keywords": ["http", "client", "httplug", "common"],
|
||||
"homepage": "http://httplug.io",
|
||||
"authors": [
|
||||
{
|
||||
"name": "Márk Sági-Kazár",
|
||||
"email": "mark.sagikazar@gmail.com"
|
||||
}
|
||||
],
|
||||
"require": {
|
||||
"php": "^7.1 || ^8.0",
|
||||
"php-http/httplug": "^2.0",
|
||||
"php-http/message-factory": "^1.0",
|
||||
"php-http/message": "^1.6",
|
||||
"psr/http-client": "^1.0",
|
||||
"psr/http-factory": "^1.0",
|
||||
"psr/http-message": "^1.0",
|
||||
"symfony/options-resolver": "^2.6 || ^3.4.20 || ~4.0.15 || ~4.1.9 || ^4.2.1 || ^5.0",
|
||||
"symfony/polyfill-php80": "^1.17"
|
||||
},
|
||||
"require-dev": {
|
||||
"doctrine/instantiator": "^1.1",
|
||||
"guzzlehttp/psr7": "^1.4",
|
||||
"nyholm/psr7": "^1.2",
|
||||
"phpspec/phpspec": "^5.1 || ^6.0",
|
||||
"phpspec/prophecy": "^1.10.2",
|
||||
"phpunit/phpunit": "^7.5.15 || ^8.5 || ^9.3"
|
||||
},
|
||||
"suggest": {
|
||||
"ext-json": "To detect JSON responses with the ContentTypePlugin",
|
||||
"ext-libxml": "To detect XML responses with the ContentTypePlugin",
|
||||
"php-http/logger-plugin": "PSR-3 Logger plugin",
|
||||
"php-http/cache-plugin": "PSR-6 Cache plugin",
|
||||
"php-http/stopwatch-plugin": "Symfony Stopwatch plugin"
|
||||
},
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
"Http\\Client\\Common\\": "src/"
|
||||
}
|
||||
},
|
||||
"autoload-dev": {
|
||||
"psr-4": {
|
||||
"spec\\Http\\Client\\Common\\": "spec/"
|
||||
}
|
||||
},
|
||||
"scripts": {
|
||||
"test": [
|
||||
"vendor/bin/phpspec run",
|
||||
"vendor/bin/phpunit"
|
||||
],
|
||||
"test-ci": [
|
||||
"vendor/bin/phpspec run -c phpspec.ci.yml",
|
||||
"vendor/bin/phpunit"
|
||||
]
|
||||
},
|
||||
"config": {
|
||||
"sort-packages": true
|
||||
},
|
||||
"extra": {
|
||||
"branch-alias": {
|
||||
"dev-master": "2.3.x-dev"
|
||||
}
|
||||
}
|
||||
}
|
||||
42
vendor/php-http/client-common/src/BatchClient.php
vendored
Normal file
42
vendor/php-http/client-common/src/BatchClient.php
vendored
Normal file
@@ -0,0 +1,42 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Http\Client\Common;
|
||||
|
||||
use Http\Client\Common\Exception\BatchException;
|
||||
use Psr\Http\Client\ClientExceptionInterface;
|
||||
use Psr\Http\Client\ClientInterface;
|
||||
|
||||
final class BatchClient implements BatchClientInterface
|
||||
{
|
||||
/**
|
||||
* @var ClientInterface
|
||||
*/
|
||||
private $client;
|
||||
|
||||
public function __construct(ClientInterface $client)
|
||||
{
|
||||
$this->client = $client;
|
||||
}
|
||||
|
||||
public function sendRequests(array $requests): BatchResult
|
||||
{
|
||||
$batchResult = new BatchResult();
|
||||
|
||||
foreach ($requests as $request) {
|
||||
try {
|
||||
$response = $this->client->sendRequest($request);
|
||||
$batchResult = $batchResult->addResponse($request, $response);
|
||||
} catch (ClientExceptionInterface $e) {
|
||||
$batchResult = $batchResult->addException($request, $e);
|
||||
}
|
||||
}
|
||||
|
||||
if ($batchResult->hasExceptions()) {
|
||||
throw new BatchException($batchResult);
|
||||
}
|
||||
|
||||
return $batchResult;
|
||||
}
|
||||
}
|
||||
34
vendor/php-http/client-common/src/BatchClientInterface.php
vendored
Normal file
34
vendor/php-http/client-common/src/BatchClientInterface.php
vendored
Normal file
@@ -0,0 +1,34 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Http\Client\Common;
|
||||
|
||||
use Http\Client\Common\Exception\BatchException;
|
||||
use Psr\Http\Message\RequestInterface;
|
||||
|
||||
/**
|
||||
* BatchClient allow to sends multiple request and retrieve a Batch Result.
|
||||
*
|
||||
* This implementation simply loops over the requests and uses sendRequest with each of them.
|
||||
*
|
||||
* @author Joel Wurtz <jwurtz@jolicode.com>
|
||||
*/
|
||||
interface BatchClientInterface
|
||||
{
|
||||
/**
|
||||
* Send several requests.
|
||||
*
|
||||
* You may not assume that the requests are executed in a particular order. If the order matters
|
||||
* for your application, use sendRequest sequentially.
|
||||
*
|
||||
* @param RequestInterface[] $requests The requests to send
|
||||
*
|
||||
* @return BatchResult Containing one result per request
|
||||
*
|
||||
* @throws BatchException If one or more requests fails. The exception gives access to the
|
||||
* BatchResult with a map of request to result for success, request to
|
||||
* exception for failures
|
||||
*/
|
||||
public function sendRequests(array $requests): BatchResult;
|
||||
}
|
||||
157
vendor/php-http/client-common/src/BatchResult.php
vendored
Normal file
157
vendor/php-http/client-common/src/BatchResult.php
vendored
Normal file
@@ -0,0 +1,157 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Http\Client\Common;
|
||||
|
||||
use Psr\Http\Client\ClientExceptionInterface;
|
||||
use Psr\Http\Message\RequestInterface;
|
||||
use Psr\Http\Message\ResponseInterface;
|
||||
|
||||
/**
|
||||
* Responses and exceptions returned from parallel request execution.
|
||||
*
|
||||
* @author Márk Sági-Kazár <mark.sagikazar@gmail.com>
|
||||
*/
|
||||
final class BatchResult
|
||||
{
|
||||
/**
|
||||
* @var \SplObjectStorage
|
||||
*/
|
||||
private $responses;
|
||||
|
||||
/**
|
||||
* @var \SplObjectStorage
|
||||
*/
|
||||
private $exceptions;
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
$this->responses = new \SplObjectStorage();
|
||||
$this->exceptions = new \SplObjectStorage();
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if there are any successful responses at all.
|
||||
*/
|
||||
public function hasResponses(): bool
|
||||
{
|
||||
return $this->responses->count() > 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns all successful responses.
|
||||
*
|
||||
* @return ResponseInterface[]
|
||||
*/
|
||||
public function getResponses(): array
|
||||
{
|
||||
$responses = [];
|
||||
|
||||
foreach ($this->responses as $request) {
|
||||
$responses[] = $this->responses[$request];
|
||||
}
|
||||
|
||||
return $responses;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if there is a successful response for a request.
|
||||
*/
|
||||
public function isSuccessful(RequestInterface $request): bool
|
||||
{
|
||||
return $this->responses->contains($request);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the response for a successful request.
|
||||
*
|
||||
* @throws \UnexpectedValueException If request was not part of the batch or failed
|
||||
*/
|
||||
public function getResponseFor(RequestInterface $request): ResponseInterface
|
||||
{
|
||||
try {
|
||||
return $this->responses[$request];
|
||||
} catch (\UnexpectedValueException $e) {
|
||||
throw new \UnexpectedValueException('Request not found', $e->getCode(), $e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a response in an immutable way.
|
||||
*
|
||||
* @return BatchResult the new BatchResult with this request-response pair added to it
|
||||
*/
|
||||
public function addResponse(RequestInterface $request, ResponseInterface $response): self
|
||||
{
|
||||
$new = clone $this;
|
||||
$new->responses->attach($request, $response);
|
||||
|
||||
return $new;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if there are any unsuccessful requests at all.
|
||||
*/
|
||||
public function hasExceptions(): bool
|
||||
{
|
||||
return $this->exceptions->count() > 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns all exceptions for the unsuccessful requests.
|
||||
*
|
||||
* @return ClientExceptionInterface[]
|
||||
*/
|
||||
public function getExceptions(): array
|
||||
{
|
||||
$exceptions = [];
|
||||
|
||||
foreach ($this->exceptions as $request) {
|
||||
$exceptions[] = $this->exceptions[$request];
|
||||
}
|
||||
|
||||
return $exceptions;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if there is an exception for a request, meaning the request failed.
|
||||
*/
|
||||
public function isFailed(RequestInterface $request): bool
|
||||
{
|
||||
return $this->exceptions->contains($request);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the exception for a failed request.
|
||||
*
|
||||
* @throws \UnexpectedValueException If request was not part of the batch or was successful
|
||||
*/
|
||||
public function getExceptionFor(RequestInterface $request): ClientExceptionInterface
|
||||
{
|
||||
try {
|
||||
return $this->exceptions[$request];
|
||||
} catch (\UnexpectedValueException $e) {
|
||||
throw new \UnexpectedValueException('Request not found', $e->getCode(), $e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds an exception in an immutable way.
|
||||
*
|
||||
* @return BatchResult the new BatchResult with this request-exception pair added to it
|
||||
*/
|
||||
public function addException(RequestInterface $request, ClientExceptionInterface $exception): self
|
||||
{
|
||||
$new = clone $this;
|
||||
$new->exceptions->attach($request, $exception);
|
||||
|
||||
return $new;
|
||||
}
|
||||
|
||||
public function __clone()
|
||||
{
|
||||
$this->responses = clone $this->responses;
|
||||
$this->exceptions = clone $this->exceptions;
|
||||
}
|
||||
}
|
||||
152
vendor/php-http/client-common/src/Deferred.php
vendored
Normal file
152
vendor/php-http/client-common/src/Deferred.php
vendored
Normal file
@@ -0,0 +1,152 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Http\Client\Common;
|
||||
|
||||
use Http\Promise\Promise;
|
||||
use Psr\Http\Client\ClientExceptionInterface;
|
||||
use Psr\Http\Message\ResponseInterface;
|
||||
|
||||
/**
|
||||
* A deferred allow to return a promise which has not been resolved yet.
|
||||
*/
|
||||
final class Deferred implements Promise
|
||||
{
|
||||
/**
|
||||
* @var ResponseInterface|null
|
||||
*/
|
||||
private $value;
|
||||
|
||||
/**
|
||||
* @var ClientExceptionInterface|null
|
||||
*/
|
||||
private $failure;
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
private $state;
|
||||
|
||||
/**
|
||||
* @var callable
|
||||
*/
|
||||
private $waitCallback;
|
||||
|
||||
/**
|
||||
* @var callable[]
|
||||
*/
|
||||
private $onFulfilledCallbacks;
|
||||
|
||||
/**
|
||||
* @var callable[]
|
||||
*/
|
||||
private $onRejectedCallbacks;
|
||||
|
||||
public function __construct(callable $waitCallback)
|
||||
{
|
||||
$this->waitCallback = $waitCallback;
|
||||
$this->state = Promise::PENDING;
|
||||
$this->onFulfilledCallbacks = [];
|
||||
$this->onRejectedCallbacks = [];
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function then(callable $onFulfilled = null, callable $onRejected = null): Promise
|
||||
{
|
||||
$deferred = new self($this->waitCallback);
|
||||
|
||||
$this->onFulfilledCallbacks[] = function (ResponseInterface $response) use ($onFulfilled, $deferred) {
|
||||
try {
|
||||
if (null !== $onFulfilled) {
|
||||
$response = $onFulfilled($response);
|
||||
}
|
||||
$deferred->resolve($response);
|
||||
} catch (ClientExceptionInterface $exception) {
|
||||
$deferred->reject($exception);
|
||||
}
|
||||
};
|
||||
|
||||
$this->onRejectedCallbacks[] = function (ClientExceptionInterface $exception) use ($onRejected, $deferred) {
|
||||
try {
|
||||
if (null !== $onRejected) {
|
||||
$response = $onRejected($exception);
|
||||
$deferred->resolve($response);
|
||||
|
||||
return;
|
||||
}
|
||||
$deferred->reject($exception);
|
||||
} catch (ClientExceptionInterface $newException) {
|
||||
$deferred->reject($newException);
|
||||
}
|
||||
};
|
||||
|
||||
return $deferred;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getState(): string
|
||||
{
|
||||
return $this->state;
|
||||
}
|
||||
|
||||
/**
|
||||
* Resolve this deferred with a Response.
|
||||
*/
|
||||
public function resolve(ResponseInterface $response): void
|
||||
{
|
||||
if (Promise::PENDING !== $this->state) {
|
||||
return;
|
||||
}
|
||||
|
||||
$this->value = $response;
|
||||
$this->state = Promise::FULFILLED;
|
||||
|
||||
foreach ($this->onFulfilledCallbacks as $onFulfilledCallback) {
|
||||
$onFulfilledCallback($response);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Reject this deferred with an Exception.
|
||||
*/
|
||||
public function reject(ClientExceptionInterface $exception): void
|
||||
{
|
||||
if (Promise::PENDING !== $this->state) {
|
||||
return;
|
||||
}
|
||||
|
||||
$this->failure = $exception;
|
||||
$this->state = Promise::REJECTED;
|
||||
|
||||
foreach ($this->onRejectedCallbacks as $onRejectedCallback) {
|
||||
$onRejectedCallback($exception);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function wait($unwrap = true)
|
||||
{
|
||||
if (Promise::PENDING === $this->state) {
|
||||
$callback = $this->waitCallback;
|
||||
$callback();
|
||||
}
|
||||
|
||||
if (!$unwrap) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (Promise::FULFILLED === $this->state) {
|
||||
return $this->value;
|
||||
}
|
||||
|
||||
/** @var ClientExceptionInterface */
|
||||
throw $this->failure;
|
||||
}
|
||||
}
|
||||
25
vendor/php-http/client-common/src/EmulatedHttpAsyncClient.php
vendored
Normal file
25
vendor/php-http/client-common/src/EmulatedHttpAsyncClient.php
vendored
Normal file
@@ -0,0 +1,25 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Http\Client\Common;
|
||||
|
||||
use Http\Client\HttpAsyncClient;
|
||||
use Http\Client\HttpClient;
|
||||
use Psr\Http\Client\ClientInterface;
|
||||
|
||||
/**
|
||||
* Emulates an async HTTP client with the help of a synchronous client.
|
||||
*
|
||||
* @author Márk Sági-Kazár <mark.sagikazar@gmail.com>
|
||||
*/
|
||||
final class EmulatedHttpAsyncClient implements HttpClient, HttpAsyncClient
|
||||
{
|
||||
use HttpAsyncClientEmulator;
|
||||
use HttpClientDecorator;
|
||||
|
||||
public function __construct(ClientInterface $httpClient)
|
||||
{
|
||||
$this->httpClient = $httpClient;
|
||||
}
|
||||
}
|
||||
24
vendor/php-http/client-common/src/EmulatedHttpClient.php
vendored
Normal file
24
vendor/php-http/client-common/src/EmulatedHttpClient.php
vendored
Normal file
@@ -0,0 +1,24 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Http\Client\Common;
|
||||
|
||||
use Http\Client\HttpAsyncClient;
|
||||
use Http\Client\HttpClient;
|
||||
|
||||
/**
|
||||
* Emulates a synchronous HTTP client with the help of an asynchronous client.
|
||||
*
|
||||
* @author Márk Sági-Kazár <mark.sagikazar@gmail.com>
|
||||
*/
|
||||
final class EmulatedHttpClient implements HttpClient, HttpAsyncClient
|
||||
{
|
||||
use HttpAsyncClientDecorator;
|
||||
use HttpClientEmulator;
|
||||
|
||||
public function __construct(HttpAsyncClient $httpAsyncClient)
|
||||
{
|
||||
$this->httpAsyncClient = $httpAsyncClient;
|
||||
}
|
||||
}
|
||||
37
vendor/php-http/client-common/src/Exception/BatchException.php
vendored
Normal file
37
vendor/php-http/client-common/src/Exception/BatchException.php
vendored
Normal file
@@ -0,0 +1,37 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Http\Client\Common\Exception;
|
||||
|
||||
use Http\Client\Common\BatchResult;
|
||||
use Http\Client\Exception\TransferException;
|
||||
|
||||
/**
|
||||
* This exception is thrown when HttpClient::sendRequests led to at least one failure.
|
||||
*
|
||||
* It gives access to a BatchResult with the request-exception and request-response pairs.
|
||||
*
|
||||
* @author Márk Sági-Kazár <mark.sagikazar@gmail.com>
|
||||
*/
|
||||
final class BatchException extends TransferException
|
||||
{
|
||||
/**
|
||||
* @var BatchResult
|
||||
*/
|
||||
private $result;
|
||||
|
||||
public function __construct(BatchResult $result)
|
||||
{
|
||||
$this->result = $result;
|
||||
parent::__construct();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the BatchResult that contains all responses and exceptions.
|
||||
*/
|
||||
public function getResult(): BatchResult
|
||||
{
|
||||
return $this->result;
|
||||
}
|
||||
}
|
||||
16
vendor/php-http/client-common/src/Exception/CircularRedirectionException.php
vendored
Normal file
16
vendor/php-http/client-common/src/Exception/CircularRedirectionException.php
vendored
Normal file
@@ -0,0 +1,16 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Http\Client\Common\Exception;
|
||||
|
||||
use Http\Client\Exception\HttpException;
|
||||
|
||||
/**
|
||||
* Thrown when circular redirection is detected.
|
||||
*
|
||||
* @author Joel Wurtz <joel.wurtz@gmail.com>
|
||||
*/
|
||||
final class CircularRedirectionException extends HttpException
|
||||
{
|
||||
}
|
||||
16
vendor/php-http/client-common/src/Exception/ClientErrorException.php
vendored
Normal file
16
vendor/php-http/client-common/src/Exception/ClientErrorException.php
vendored
Normal file
@@ -0,0 +1,16 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Http\Client\Common\Exception;
|
||||
|
||||
use Http\Client\Exception\HttpException;
|
||||
|
||||
/**
|
||||
* Thrown when there is a client error (4xx).
|
||||
*
|
||||
* @author Joel Wurtz <joel.wurtz@gmail.com>
|
||||
*/
|
||||
final class ClientErrorException extends HttpException
|
||||
{
|
||||
}
|
||||
33
vendor/php-http/client-common/src/Exception/HttpClientNoMatchException.php
vendored
Normal file
33
vendor/php-http/client-common/src/Exception/HttpClientNoMatchException.php
vendored
Normal file
@@ -0,0 +1,33 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Http\Client\Common\Exception;
|
||||
|
||||
use Http\Client\Exception\TransferException;
|
||||
use Psr\Http\Message\RequestInterface;
|
||||
|
||||
/**
|
||||
* Thrown when a http client match in the HTTPClientRouter.
|
||||
*
|
||||
* @author Tobias Nyholm <tobias.nyholm@gmail.com>
|
||||
*/
|
||||
final class HttpClientNoMatchException extends TransferException
|
||||
{
|
||||
/**
|
||||
* @var RequestInterface
|
||||
*/
|
||||
private $request;
|
||||
|
||||
public function __construct(string $message, RequestInterface $request, \Exception $previous = null)
|
||||
{
|
||||
$this->request = $request;
|
||||
|
||||
parent::__construct($message, 0, $previous);
|
||||
}
|
||||
|
||||
public function getRequest(): RequestInterface
|
||||
{
|
||||
return $this->request;
|
||||
}
|
||||
}
|
||||
16
vendor/php-http/client-common/src/Exception/HttpClientNotFoundException.php
vendored
Normal file
16
vendor/php-http/client-common/src/Exception/HttpClientNotFoundException.php
vendored
Normal file
@@ -0,0 +1,16 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Http\Client\Common\Exception;
|
||||
|
||||
use Http\Client\Exception\TransferException;
|
||||
|
||||
/**
|
||||
* Thrown when a http client cannot be chosen in a pool.
|
||||
*
|
||||
* @author Joel Wurtz <joel.wurtz@gmail.com>
|
||||
*/
|
||||
final class HttpClientNotFoundException extends TransferException
|
||||
{
|
||||
}
|
||||
16
vendor/php-http/client-common/src/Exception/LoopException.php
vendored
Normal file
16
vendor/php-http/client-common/src/Exception/LoopException.php
vendored
Normal file
@@ -0,0 +1,16 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Http\Client\Common\Exception;
|
||||
|
||||
use Http\Client\Exception\RequestException;
|
||||
|
||||
/**
|
||||
* Thrown when the Plugin Client detects an endless loop.
|
||||
*
|
||||
* @author Joel Wurtz <joel.wurtz@gmail.com>
|
||||
*/
|
||||
final class LoopException extends RequestException
|
||||
{
|
||||
}
|
||||
16
vendor/php-http/client-common/src/Exception/MultipleRedirectionException.php
vendored
Normal file
16
vendor/php-http/client-common/src/Exception/MultipleRedirectionException.php
vendored
Normal file
@@ -0,0 +1,16 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Http\Client\Common\Exception;
|
||||
|
||||
use Http\Client\Exception\HttpException;
|
||||
|
||||
/**
|
||||
* Redirect location cannot be chosen.
|
||||
*
|
||||
* @author Joel Wurtz <joel.wurtz@gmail.com>
|
||||
*/
|
||||
final class MultipleRedirectionException extends HttpException
|
||||
{
|
||||
}
|
||||
16
vendor/php-http/client-common/src/Exception/ServerErrorException.php
vendored
Normal file
16
vendor/php-http/client-common/src/Exception/ServerErrorException.php
vendored
Normal file
@@ -0,0 +1,16 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Http\Client\Common\Exception;
|
||||
|
||||
use Http\Client\Exception\HttpException;
|
||||
|
||||
/**
|
||||
* Thrown when there is a server error (5xx).
|
||||
*
|
||||
* @author Joel Wurtz <joel.wurtz@gmail.com>
|
||||
*/
|
||||
final class ServerErrorException extends HttpException
|
||||
{
|
||||
}
|
||||
36
vendor/php-http/client-common/src/FlexibleHttpClient.php
vendored
Normal file
36
vendor/php-http/client-common/src/FlexibleHttpClient.php
vendored
Normal file
@@ -0,0 +1,36 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Http\Client\Common;
|
||||
|
||||
use Http\Client\HttpAsyncClient;
|
||||
use Http\Client\HttpClient;
|
||||
use Psr\Http\Client\ClientInterface;
|
||||
|
||||
/**
|
||||
* A flexible http client, which implements both interface and will emulate
|
||||
* one contract, the other, or none at all depending on the injected client contract.
|
||||
*
|
||||
* @author Joel Wurtz <joel.wurtz@gmail.com>
|
||||
*/
|
||||
final class FlexibleHttpClient implements HttpClient, HttpAsyncClient
|
||||
{
|
||||
use HttpClientDecorator;
|
||||
use HttpAsyncClientDecorator;
|
||||
|
||||
/**
|
||||
* @param ClientInterface|HttpAsyncClient $client
|
||||
*/
|
||||
public function __construct($client)
|
||||
{
|
||||
if (!$client instanceof ClientInterface && !$client instanceof HttpAsyncClient) {
|
||||
throw new \TypeError(
|
||||
sprintf('%s::__construct(): Argument #1 ($client) must be of type %s|%s, %s given', self::class, ClientInterface::class, HttpAsyncClient::class, get_debug_type($client))
|
||||
);
|
||||
}
|
||||
|
||||
$this->httpClient = $client instanceof ClientInterface ? $client : new EmulatedHttpClient($client);
|
||||
$this->httpAsyncClient = $client instanceof HttpAsyncClient ? $client : new EmulatedHttpAsyncClient($client);
|
||||
}
|
||||
}
|
||||
31
vendor/php-http/client-common/src/HttpAsyncClientDecorator.php
vendored
Normal file
31
vendor/php-http/client-common/src/HttpAsyncClientDecorator.php
vendored
Normal file
@@ -0,0 +1,31 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Http\Client\Common;
|
||||
|
||||
use Http\Client\HttpAsyncClient;
|
||||
use Psr\Http\Message\RequestInterface;
|
||||
|
||||
/**
|
||||
* Decorates an HTTP Async Client.
|
||||
*
|
||||
* @author Márk Sági-Kazár <mark.sagikazar@gmail.com>
|
||||
*/
|
||||
trait HttpAsyncClientDecorator
|
||||
{
|
||||
/**
|
||||
* @var HttpAsyncClient
|
||||
*/
|
||||
protected $httpAsyncClient;
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*
|
||||
* @see HttpAsyncClient::sendAsyncRequest
|
||||
*/
|
||||
public function sendAsyncRequest(RequestInterface $request)
|
||||
{
|
||||
return $this->httpAsyncClient->sendAsyncRequest($request);
|
||||
}
|
||||
}
|
||||
39
vendor/php-http/client-common/src/HttpAsyncClientEmulator.php
vendored
Normal file
39
vendor/php-http/client-common/src/HttpAsyncClientEmulator.php
vendored
Normal file
@@ -0,0 +1,39 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Http\Client\Common;
|
||||
|
||||
use Http\Client\Exception;
|
||||
use Http\Client\Promise;
|
||||
use Psr\Http\Message\RequestInterface;
|
||||
use Psr\Http\Message\ResponseInterface;
|
||||
|
||||
/**
|
||||
* Emulates an HTTP Async Client in an HTTP Client.
|
||||
*
|
||||
* @author Márk Sági-Kazár <mark.sagikazar@gmail.com>
|
||||
*/
|
||||
trait HttpAsyncClientEmulator
|
||||
{
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*
|
||||
* @see HttpClient::sendRequest
|
||||
*/
|
||||
abstract public function sendRequest(RequestInterface $request): ResponseInterface;
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*
|
||||
* @see HttpAsyncClient::sendAsyncRequest
|
||||
*/
|
||||
public function sendAsyncRequest(RequestInterface $request)
|
||||
{
|
||||
try {
|
||||
return new Promise\HttpFulfilledPromise($this->sendRequest($request));
|
||||
} catch (Exception $e) {
|
||||
return new Promise\HttpRejectedPromise($e);
|
||||
}
|
||||
}
|
||||
}
|
||||
32
vendor/php-http/client-common/src/HttpClientDecorator.php
vendored
Normal file
32
vendor/php-http/client-common/src/HttpClientDecorator.php
vendored
Normal file
@@ -0,0 +1,32 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Http\Client\Common;
|
||||
|
||||
use Psr\Http\Client\ClientInterface;
|
||||
use Psr\Http\Message\RequestInterface;
|
||||
use Psr\Http\Message\ResponseInterface;
|
||||
|
||||
/**
|
||||
* Decorates an HTTP Client.
|
||||
*
|
||||
* @author Márk Sági-Kazár <mark.sagikazar@gmail.com>
|
||||
*/
|
||||
trait HttpClientDecorator
|
||||
{
|
||||
/**
|
||||
* @var ClientInterface
|
||||
*/
|
||||
protected $httpClient;
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*
|
||||
* @see ClientInterface::sendRequest
|
||||
*/
|
||||
public function sendRequest(RequestInterface $request): ResponseInterface
|
||||
{
|
||||
return $this->httpClient->sendRequest($request);
|
||||
}
|
||||
}
|
||||
35
vendor/php-http/client-common/src/HttpClientEmulator.php
vendored
Normal file
35
vendor/php-http/client-common/src/HttpClientEmulator.php
vendored
Normal file
@@ -0,0 +1,35 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Http\Client\Common;
|
||||
|
||||
use Psr\Http\Message\RequestInterface;
|
||||
use Psr\Http\Message\ResponseInterface;
|
||||
|
||||
/**
|
||||
* Emulates an HTTP Client in an HTTP Async Client.
|
||||
*
|
||||
* @author Márk Sági-Kazár <mark.sagikazar@gmail.com>
|
||||
*/
|
||||
trait HttpClientEmulator
|
||||
{
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*
|
||||
* @see HttpClient::sendRequest
|
||||
*/
|
||||
public function sendRequest(RequestInterface $request): ResponseInterface
|
||||
{
|
||||
$promise = $this->sendAsyncRequest($request);
|
||||
|
||||
return $promise->wait();
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*
|
||||
* @see HttpAsyncClient::sendAsyncRequest
|
||||
*/
|
||||
abstract public function sendAsyncRequest(RequestInterface $request);
|
||||
}
|
||||
24
vendor/php-http/client-common/src/HttpClientPool.php
vendored
Normal file
24
vendor/php-http/client-common/src/HttpClientPool.php
vendored
Normal file
@@ -0,0 +1,24 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Http\Client\Common;
|
||||
|
||||
use Http\Client\Common\HttpClientPool\HttpClientPoolItem;
|
||||
use Http\Client\HttpAsyncClient;
|
||||
use Http\Client\HttpClient;
|
||||
use Psr\Http\Client\ClientInterface;
|
||||
|
||||
/**
|
||||
* A http client pool allows to send requests on a pool of different http client using a specific strategy (least used,
|
||||
* round robin, ...).
|
||||
*/
|
||||
interface HttpClientPool extends HttpAsyncClient, HttpClient
|
||||
{
|
||||
/**
|
||||
* Add a client to the pool.
|
||||
*
|
||||
* @param ClientInterface|HttpAsyncClient|HttpClientPoolItem $client
|
||||
*/
|
||||
public function addHttpClient($client): void;
|
||||
}
|
||||
70
vendor/php-http/client-common/src/HttpClientPool/HttpClientPool.php
vendored
Normal file
70
vendor/php-http/client-common/src/HttpClientPool/HttpClientPool.php
vendored
Normal file
@@ -0,0 +1,70 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Http\Client\Common\HttpClientPool;
|
||||
|
||||
use Http\Client\Common\Exception\HttpClientNotFoundException;
|
||||
use Http\Client\Common\HttpClientPool as HttpClientPoolInterface;
|
||||
use Http\Client\HttpAsyncClient;
|
||||
use Psr\Http\Client\ClientInterface;
|
||||
use Psr\Http\Message\RequestInterface;
|
||||
use Psr\Http\Message\ResponseInterface;
|
||||
|
||||
/**
|
||||
* A http client pool allows to send requests on a pool of different http client using a specific strategy (least used,
|
||||
* round robin, ...).
|
||||
*/
|
||||
abstract class HttpClientPool implements HttpClientPoolInterface
|
||||
{
|
||||
/**
|
||||
* @var HttpClientPoolItem[]
|
||||
*/
|
||||
protected $clientPool = [];
|
||||
|
||||
/**
|
||||
* Add a client to the pool.
|
||||
*
|
||||
* @param ClientInterface|HttpAsyncClient $client
|
||||
*/
|
||||
public function addHttpClient($client): void
|
||||
{
|
||||
// no need to check for HttpClientPoolItem here, since it extends the other interfaces
|
||||
if (!$client instanceof ClientInterface && !$client instanceof HttpAsyncClient) {
|
||||
throw new \TypeError(
|
||||
sprintf('%s::addHttpClient(): Argument #1 ($client) must be of type %s|%s, %s given', self::class, ClientInterface::class, HttpAsyncClient::class, get_debug_type($client))
|
||||
);
|
||||
}
|
||||
|
||||
if (!$client instanceof HttpClientPoolItem) {
|
||||
$client = new HttpClientPoolItem($client);
|
||||
}
|
||||
|
||||
$this->clientPool[] = $client;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return an http client given a specific strategy.
|
||||
*
|
||||
* @throws HttpClientNotFoundException When no http client has been found into the pool
|
||||
*
|
||||
* @return HttpClientPoolItem Return a http client that can do both sync or async
|
||||
*/
|
||||
abstract protected function chooseHttpClient(): HttpClientPoolItem;
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function sendAsyncRequest(RequestInterface $request)
|
||||
{
|
||||
return $this->chooseHttpClient()->sendAsyncRequest($request);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function sendRequest(RequestInterface $request): ResponseInterface
|
||||
{
|
||||
return $this->chooseHttpClient()->sendRequest($request);
|
||||
}
|
||||
}
|
||||
181
vendor/php-http/client-common/src/HttpClientPool/HttpClientPoolItem.php
vendored
Normal file
181
vendor/php-http/client-common/src/HttpClientPool/HttpClientPoolItem.php
vendored
Normal file
@@ -0,0 +1,181 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Http\Client\Common\HttpClientPool;
|
||||
|
||||
use Http\Client\Common\FlexibleHttpClient;
|
||||
use Http\Client\Exception;
|
||||
use Http\Client\HttpAsyncClient;
|
||||
use Http\Client\HttpClient;
|
||||
use Psr\Http\Client\ClientInterface;
|
||||
use Psr\Http\Message\RequestInterface;
|
||||
use Psr\Http\Message\ResponseInterface;
|
||||
|
||||
/**
|
||||
* A HttpClientPoolItem represent a HttpClient inside a Pool.
|
||||
*
|
||||
* It is disabled when a request failed and can be reenabled after a certain number of seconds.
|
||||
* It also keep tracks of the current number of open requests the client is currently being sending
|
||||
* (only usable for async method).
|
||||
*
|
||||
* This class is used internally in the client pools and is not supposed to be used anywhere else.
|
||||
*
|
||||
* @final
|
||||
*
|
||||
* @internal
|
||||
*
|
||||
* @author Joel Wurtz <joel.wurtz@gmail.com>
|
||||
*/
|
||||
class HttpClientPoolItem implements HttpClient, HttpAsyncClient
|
||||
{
|
||||
/**
|
||||
* @var int Number of request this client is currently sending
|
||||
*/
|
||||
private $sendingRequestCount = 0;
|
||||
|
||||
/**
|
||||
* @var \DateTime|null Time when this client has been disabled or null if enable
|
||||
*/
|
||||
private $disabledAt;
|
||||
|
||||
/**
|
||||
* Number of seconds until this client is enabled again after an error.
|
||||
*
|
||||
* null: never reenable this client.
|
||||
*
|
||||
* @var int|null
|
||||
*/
|
||||
private $reenableAfter;
|
||||
|
||||
/**
|
||||
* @var FlexibleHttpClient A http client responding to async and sync request
|
||||
*/
|
||||
private $client;
|
||||
|
||||
/**
|
||||
* @param ClientInterface|HttpAsyncClient $client
|
||||
* @param int|null $reenableAfter Number of seconds until this client is enabled again after an error
|
||||
*/
|
||||
public function __construct($client, int $reenableAfter = null)
|
||||
{
|
||||
if (!$client instanceof ClientInterface && !$client instanceof HttpAsyncClient) {
|
||||
throw new \TypeError(
|
||||
sprintf('%s::__construct(): Argument #1 ($client) must be of type %s|%s, %s given', self::class, ClientInterface::class, HttpAsyncClient::class, get_debug_type($client))
|
||||
);
|
||||
}
|
||||
|
||||
$this->client = new FlexibleHttpClient($client);
|
||||
$this->reenableAfter = $reenableAfter;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function sendRequest(RequestInterface $request): ResponseInterface
|
||||
{
|
||||
if ($this->isDisabled()) {
|
||||
throw new Exception\RequestException('Cannot send the request as this client has been disabled', $request);
|
||||
}
|
||||
|
||||
try {
|
||||
$this->incrementRequestCount();
|
||||
$response = $this->client->sendRequest($request);
|
||||
$this->decrementRequestCount();
|
||||
} catch (Exception $e) {
|
||||
$this->disable();
|
||||
$this->decrementRequestCount();
|
||||
|
||||
throw $e;
|
||||
}
|
||||
|
||||
return $response;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function sendAsyncRequest(RequestInterface $request)
|
||||
{
|
||||
if ($this->isDisabled()) {
|
||||
throw new Exception\RequestException('Cannot send the request as this client has been disabled', $request);
|
||||
}
|
||||
|
||||
$this->incrementRequestCount();
|
||||
|
||||
return $this->client->sendAsyncRequest($request)->then(function ($response) {
|
||||
$this->decrementRequestCount();
|
||||
|
||||
return $response;
|
||||
}, function ($exception) {
|
||||
$this->disable();
|
||||
$this->decrementRequestCount();
|
||||
|
||||
throw $exception;
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Whether this client is disabled or not.
|
||||
*
|
||||
* If the client was disabled, calling this method checks if the client can
|
||||
* be reenabled and if so enables it.
|
||||
*/
|
||||
public function isDisabled(): bool
|
||||
{
|
||||
if (null !== $this->reenableAfter && null !== $this->disabledAt) {
|
||||
// Reenable after a certain time
|
||||
$now = new \DateTime();
|
||||
|
||||
if (($now->getTimestamp() - $this->disabledAt->getTimestamp()) >= $this->reenableAfter) {
|
||||
$this->enable();
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
return null !== $this->disabledAt;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get current number of request that are currently being sent by the underlying HTTP client.
|
||||
*/
|
||||
public function getSendingRequestCount(): int
|
||||
{
|
||||
return $this->sendingRequestCount;
|
||||
}
|
||||
|
||||
/**
|
||||
* Increment the request count.
|
||||
*/
|
||||
private function incrementRequestCount(): void
|
||||
{
|
||||
++$this->sendingRequestCount;
|
||||
}
|
||||
|
||||
/**
|
||||
* Decrement the request count.
|
||||
*/
|
||||
private function decrementRequestCount(): void
|
||||
{
|
||||
--$this->sendingRequestCount;
|
||||
}
|
||||
|
||||
/**
|
||||
* Enable the current client.
|
||||
*/
|
||||
private function enable(): void
|
||||
{
|
||||
$this->disabledAt = null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Disable the current client.
|
||||
*/
|
||||
private function disable(): void
|
||||
{
|
||||
$this->disabledAt = new \DateTime('now');
|
||||
}
|
||||
}
|
||||
45
vendor/php-http/client-common/src/HttpClientPool/LeastUsedClientPool.php
vendored
Normal file
45
vendor/php-http/client-common/src/HttpClientPool/LeastUsedClientPool.php
vendored
Normal file
@@ -0,0 +1,45 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Http\Client\Common\HttpClientPool;
|
||||
|
||||
use Http\Client\Common\Exception\HttpClientNotFoundException;
|
||||
|
||||
/**
|
||||
* LeastUsedClientPool will choose the client with the less current request in the pool.
|
||||
*
|
||||
* This strategy is only useful when doing async request
|
||||
*
|
||||
* @author Joel Wurtz <joel.wurtz@gmail.com>
|
||||
*/
|
||||
final class LeastUsedClientPool extends HttpClientPool
|
||||
{
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function chooseHttpClient(): HttpClientPoolItem
|
||||
{
|
||||
$clientPool = array_filter($this->clientPool, function (HttpClientPoolItem $clientPoolItem) {
|
||||
return !$clientPoolItem->isDisabled();
|
||||
});
|
||||
|
||||
if (0 === count($clientPool)) {
|
||||
throw new HttpClientNotFoundException('Cannot choose a http client as there is no one present in the pool');
|
||||
}
|
||||
|
||||
usort($clientPool, function (HttpClientPoolItem $clientA, HttpClientPoolItem $clientB) {
|
||||
if ($clientA->getSendingRequestCount() === $clientB->getSendingRequestCount()) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
if ($clientA->getSendingRequestCount() < $clientB->getSendingRequestCount()) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 1;
|
||||
});
|
||||
|
||||
return reset($clientPool);
|
||||
}
|
||||
}
|
||||
31
vendor/php-http/client-common/src/HttpClientPool/RandomClientPool.php
vendored
Normal file
31
vendor/php-http/client-common/src/HttpClientPool/RandomClientPool.php
vendored
Normal file
@@ -0,0 +1,31 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Http\Client\Common\HttpClientPool;
|
||||
|
||||
use Http\Client\Common\Exception\HttpClientNotFoundException;
|
||||
|
||||
/**
|
||||
* RoundRobinClientPool will choose the next client in the pool.
|
||||
*
|
||||
* @author Joel Wurtz <joel.wurtz@gmail.com>
|
||||
*/
|
||||
final class RandomClientPool extends HttpClientPool
|
||||
{
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function chooseHttpClient(): HttpClientPoolItem
|
||||
{
|
||||
$clientPool = array_filter($this->clientPool, function (HttpClientPoolItem $clientPoolItem) {
|
||||
return !$clientPoolItem->isDisabled();
|
||||
});
|
||||
|
||||
if (0 === count($clientPool)) {
|
||||
throw new HttpClientNotFoundException('Cannot choose a http client as there is no one present in the pool');
|
||||
}
|
||||
|
||||
return $clientPool[array_rand($clientPool)];
|
||||
}
|
||||
}
|
||||
42
vendor/php-http/client-common/src/HttpClientPool/RoundRobinClientPool.php
vendored
Normal file
42
vendor/php-http/client-common/src/HttpClientPool/RoundRobinClientPool.php
vendored
Normal file
@@ -0,0 +1,42 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Http\Client\Common\HttpClientPool;
|
||||
|
||||
use Http\Client\Common\Exception\HttpClientNotFoundException;
|
||||
|
||||
/**
|
||||
* RoundRobinClientPool will choose the next client in the pool.
|
||||
*
|
||||
* @author Joel Wurtz <joel.wurtz@gmail.com>
|
||||
*/
|
||||
final class RoundRobinClientPool extends HttpClientPool
|
||||
{
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function chooseHttpClient(): HttpClientPoolItem
|
||||
{
|
||||
$last = current($this->clientPool);
|
||||
|
||||
do {
|
||||
$client = next($this->clientPool);
|
||||
|
||||
if (false === $client) {
|
||||
$client = reset($this->clientPool);
|
||||
|
||||
if (false === $client) {
|
||||
throw new HttpClientNotFoundException('Cannot choose a http client as there is no one present in the pool');
|
||||
}
|
||||
}
|
||||
|
||||
// Case when there is only one and the last one has been disabled
|
||||
if ($last === $client && $client->isDisabled()) {
|
||||
throw new HttpClientNotFoundException('Cannot choose a http client as there is no one enabled in the pool');
|
||||
}
|
||||
} while ($client->isDisabled());
|
||||
|
||||
return $client;
|
||||
}
|
||||
}
|
||||
74
vendor/php-http/client-common/src/HttpClientRouter.php
vendored
Normal file
74
vendor/php-http/client-common/src/HttpClientRouter.php
vendored
Normal file
@@ -0,0 +1,74 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Http\Client\Common;
|
||||
|
||||
use Http\Client\Common\Exception\HttpClientNoMatchException;
|
||||
use Http\Client\HttpAsyncClient;
|
||||
use Http\Message\RequestMatcher;
|
||||
use Psr\Http\Client\ClientInterface;
|
||||
use Psr\Http\Message\RequestInterface;
|
||||
use Psr\Http\Message\ResponseInterface;
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*
|
||||
* @author Joel Wurtz <joel.wurtz@gmail.com>
|
||||
*/
|
||||
final class HttpClientRouter implements HttpClientRouterInterface
|
||||
{
|
||||
/**
|
||||
* @var (array{matcher: RequestMatcher, client: FlexibleHttpClient})[]
|
||||
*/
|
||||
private $clients = [];
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function sendRequest(RequestInterface $request): ResponseInterface
|
||||
{
|
||||
return $this->chooseHttpClient($request)->sendRequest($request);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function sendAsyncRequest(RequestInterface $request)
|
||||
{
|
||||
return $this->chooseHttpClient($request)->sendAsyncRequest($request);
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a client to the router.
|
||||
*
|
||||
* @param ClientInterface|HttpAsyncClient $client
|
||||
*/
|
||||
public function addClient($client, RequestMatcher $requestMatcher): void
|
||||
{
|
||||
if (!$client instanceof ClientInterface && !$client instanceof HttpAsyncClient) {
|
||||
throw new \TypeError(
|
||||
sprintf('%s::addClient(): Argument #1 ($client) must be of type %s|%s, %s given', self::class, ClientInterface::class, HttpAsyncClient::class, get_debug_type($client))
|
||||
);
|
||||
}
|
||||
|
||||
$this->clients[] = [
|
||||
'matcher' => $requestMatcher,
|
||||
'client' => new FlexibleHttpClient($client),
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Choose an HTTP client given a specific request.
|
||||
*/
|
||||
private function chooseHttpClient(RequestInterface $request): FlexibleHttpClient
|
||||
{
|
||||
foreach ($this->clients as $client) {
|
||||
if ($client['matcher']->matches($request)) {
|
||||
return $client['client'];
|
||||
}
|
||||
}
|
||||
|
||||
throw new HttpClientNoMatchException('No client found for the specified request', $request);
|
||||
}
|
||||
}
|
||||
27
vendor/php-http/client-common/src/HttpClientRouterInterface.php
vendored
Normal file
27
vendor/php-http/client-common/src/HttpClientRouterInterface.php
vendored
Normal file
@@ -0,0 +1,27 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Http\Client\Common;
|
||||
|
||||
use Http\Client\HttpAsyncClient;
|
||||
use Http\Client\HttpClient;
|
||||
use Http\Message\RequestMatcher;
|
||||
use Psr\Http\Client\ClientInterface;
|
||||
|
||||
/**
|
||||
* Route a request to a specific client in the stack based using a RequestMatcher.
|
||||
*
|
||||
* This is not a HttpClientPool client because it uses a matcher to select the client.
|
||||
*
|
||||
* @author Joel Wurtz <joel.wurtz@gmail.com>
|
||||
*/
|
||||
interface HttpClientRouterInterface extends HttpClient, HttpAsyncClient
|
||||
{
|
||||
/**
|
||||
* Add a client to the router.
|
||||
*
|
||||
* @param ClientInterface|HttpAsyncClient $client
|
||||
*/
|
||||
public function addClient($client, RequestMatcher $requestMatcher): void;
|
||||
}
|
||||
150
vendor/php-http/client-common/src/HttpMethodsClient.php
vendored
Normal file
150
vendor/php-http/client-common/src/HttpMethodsClient.php
vendored
Normal file
@@ -0,0 +1,150 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Http\Client\Common;
|
||||
|
||||
use Http\Message\RequestFactory;
|
||||
use Psr\Http\Client\ClientInterface;
|
||||
use Psr\Http\Message\RequestFactoryInterface;
|
||||
use Psr\Http\Message\RequestInterface;
|
||||
use Psr\Http\Message\ResponseInterface;
|
||||
use Psr\Http\Message\StreamFactoryInterface;
|
||||
use Psr\Http\Message\StreamInterface;
|
||||
use Psr\Http\Message\UriInterface;
|
||||
|
||||
final class HttpMethodsClient implements HttpMethodsClientInterface
|
||||
{
|
||||
/**
|
||||
* @var ClientInterface
|
||||
*/
|
||||
private $httpClient;
|
||||
|
||||
/**
|
||||
* @var RequestFactory|RequestFactoryInterface
|
||||
*/
|
||||
private $requestFactory;
|
||||
|
||||
/**
|
||||
* @var StreamFactoryInterface|null
|
||||
*/
|
||||
private $streamFactory;
|
||||
|
||||
/**
|
||||
* @param RequestFactory|RequestFactoryInterface $requestFactory
|
||||
*/
|
||||
public function __construct(ClientInterface $httpClient, $requestFactory, StreamFactoryInterface $streamFactory = null)
|
||||
{
|
||||
if (!$requestFactory instanceof RequestFactory && !$requestFactory instanceof RequestFactoryInterface) {
|
||||
throw new \TypeError(
|
||||
sprintf('%s::__construct(): Argument #2 ($requestFactory) must be of type %s|%s, %s given', self::class, RequestFactory::class, RequestFactoryInterface::class, get_debug_type($requestFactory))
|
||||
);
|
||||
}
|
||||
|
||||
if (!$requestFactory instanceof RequestFactory && null === $streamFactory) {
|
||||
@trigger_error(sprintf('Passing a %s without a %s to %s::__construct() is deprecated as of version 2.3 and will be disallowed in version 3.0. A stream factory is required to create a request with a non-empty string body.', RequestFactoryInterface::class, StreamFactoryInterface::class, self::class));
|
||||
}
|
||||
|
||||
$this->httpClient = $httpClient;
|
||||
$this->requestFactory = $requestFactory;
|
||||
$this->streamFactory = $streamFactory;
|
||||
}
|
||||
|
||||
public function get($uri, array $headers = []): ResponseInterface
|
||||
{
|
||||
return $this->send('GET', $uri, $headers, null);
|
||||
}
|
||||
|
||||
public function head($uri, array $headers = []): ResponseInterface
|
||||
{
|
||||
return $this->send('HEAD', $uri, $headers, null);
|
||||
}
|
||||
|
||||
public function trace($uri, array $headers = []): ResponseInterface
|
||||
{
|
||||
return $this->send('TRACE', $uri, $headers, null);
|
||||
}
|
||||
|
||||
public function post($uri, array $headers = [], $body = null): ResponseInterface
|
||||
{
|
||||
return $this->send('POST', $uri, $headers, $body);
|
||||
}
|
||||
|
||||
public function put($uri, array $headers = [], $body = null): ResponseInterface
|
||||
{
|
||||
return $this->send('PUT', $uri, $headers, $body);
|
||||
}
|
||||
|
||||
public function patch($uri, array $headers = [], $body = null): ResponseInterface
|
||||
{
|
||||
return $this->send('PATCH', $uri, $headers, $body);
|
||||
}
|
||||
|
||||
public function delete($uri, array $headers = [], $body = null): ResponseInterface
|
||||
{
|
||||
return $this->send('DELETE', $uri, $headers, $body);
|
||||
}
|
||||
|
||||
public function options($uri, array $headers = [], $body = null): ResponseInterface
|
||||
{
|
||||
return $this->send('OPTIONS', $uri, $headers, $body);
|
||||
}
|
||||
|
||||
public function send(string $method, $uri, array $headers = [], $body = null): ResponseInterface
|
||||
{
|
||||
if (!is_string($uri) && !$uri instanceof UriInterface) {
|
||||
throw new \TypeError(
|
||||
sprintf('%s::send(): Argument #2 ($uri) must be of type string|%s, %s given', self::class, UriInterface::class, get_debug_type($uri))
|
||||
);
|
||||
}
|
||||
|
||||
if (!is_string($body) && !$body instanceof StreamInterface && null !== $body) {
|
||||
throw new \TypeError(
|
||||
sprintf('%s::send(): Argument #4 ($body) must be of type string|%s|null, %s given', self::class, StreamInterface::class, get_debug_type($body))
|
||||
);
|
||||
}
|
||||
|
||||
return $this->sendRequest(
|
||||
self::createRequest($method, $uri, $headers, $body)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string|UriInterface $uri
|
||||
* @param string|StreamInterface|null $body
|
||||
*/
|
||||
private function createRequest(string $method, $uri, array $headers = [], $body = null): RequestInterface
|
||||
{
|
||||
if ($this->requestFactory instanceof RequestFactory) {
|
||||
return $this->requestFactory->createRequest(
|
||||
$method,
|
||||
$uri,
|
||||
$headers,
|
||||
$body
|
||||
);
|
||||
}
|
||||
|
||||
if (is_string($body) && '' !== $body && null === $this->streamFactory) {
|
||||
throw new \RuntimeException('Cannot create request: A stream factory is required to create a request with a non-empty string body.');
|
||||
}
|
||||
|
||||
$request = $this->requestFactory->createRequest($method, $uri);
|
||||
|
||||
foreach ($headers as $key => $value) {
|
||||
$request = $request->withHeader($key, $value);
|
||||
}
|
||||
|
||||
if (null !== $body && '' !== $body) {
|
||||
$request = $request->withBody(
|
||||
is_string($body) ? $this->streamFactory->createStream($body) : $body
|
||||
);
|
||||
}
|
||||
|
||||
return $request;
|
||||
}
|
||||
|
||||
public function sendRequest(RequestInterface $request): ResponseInterface
|
||||
{
|
||||
return $this->httpClient->sendRequest($request);
|
||||
}
|
||||
}
|
||||
116
vendor/php-http/client-common/src/HttpMethodsClientInterface.php
vendored
Normal file
116
vendor/php-http/client-common/src/HttpMethodsClientInterface.php
vendored
Normal file
@@ -0,0 +1,116 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Http\Client\Common;
|
||||
|
||||
use Http\Client\Exception;
|
||||
use Http\Client\HttpClient;
|
||||
use Psr\Http\Message\ResponseInterface;
|
||||
use Psr\Http\Message\StreamInterface;
|
||||
use Psr\Http\Message\UriInterface;
|
||||
|
||||
/**
|
||||
* Convenience HTTP client that integrates the MessageFactory in order to send
|
||||
* requests in the following form:.
|
||||
*
|
||||
* $client
|
||||
* ->get('/foo')
|
||||
* ->post('/bar')
|
||||
* ;
|
||||
*
|
||||
* The client also exposes the sendRequest methods of the wrapped HttpClient.
|
||||
*
|
||||
* @author Márk Sági-Kazár <mark.sagikazar@gmail.com>
|
||||
* @author David Buchmann <mail@davidbu.ch>
|
||||
*/
|
||||
interface HttpMethodsClientInterface extends HttpClient
|
||||
{
|
||||
/**
|
||||
* Sends a GET request.
|
||||
*
|
||||
* @param string|UriInterface $uri
|
||||
*
|
||||
* @throws Exception
|
||||
*/
|
||||
public function get($uri, array $headers = []): ResponseInterface;
|
||||
|
||||
/**
|
||||
* Sends an HEAD request.
|
||||
*
|
||||
* @param string|UriInterface $uri
|
||||
*
|
||||
* @throws Exception
|
||||
*/
|
||||
public function head($uri, array $headers = []): ResponseInterface;
|
||||
|
||||
/**
|
||||
* Sends a TRACE request.
|
||||
*
|
||||
* @param string|UriInterface $uri
|
||||
*
|
||||
* @throws Exception
|
||||
*/
|
||||
public function trace($uri, array $headers = []): ResponseInterface;
|
||||
|
||||
/**
|
||||
* Sends a POST request.
|
||||
*
|
||||
* @param string|UriInterface $uri
|
||||
* @param string|StreamInterface|null $body
|
||||
*
|
||||
* @throws Exception
|
||||
*/
|
||||
public function post($uri, array $headers = [], $body = null): ResponseInterface;
|
||||
|
||||
/**
|
||||
* Sends a PUT request.
|
||||
*
|
||||
* @param string|UriInterface $uri
|
||||
* @param string|StreamInterface|null $body
|
||||
*
|
||||
* @throws Exception
|
||||
*/
|
||||
public function put($uri, array $headers = [], $body = null): ResponseInterface;
|
||||
|
||||
/**
|
||||
* Sends a PATCH request.
|
||||
*
|
||||
* @param string|UriInterface $uri
|
||||
* @param string|StreamInterface|null $body
|
||||
*
|
||||
* @throws Exception
|
||||
*/
|
||||
public function patch($uri, array $headers = [], $body = null): ResponseInterface;
|
||||
|
||||
/**
|
||||
* Sends a DELETE request.
|
||||
*
|
||||
* @param string|UriInterface $uri
|
||||
* @param string|StreamInterface|null $body
|
||||
*
|
||||
* @throws Exception
|
||||
*/
|
||||
public function delete($uri, array $headers = [], $body = null): ResponseInterface;
|
||||
|
||||
/**
|
||||
* Sends an OPTIONS request.
|
||||
*
|
||||
* @param string|UriInterface $uri
|
||||
* @param string|StreamInterface|null $body
|
||||
*
|
||||
* @throws Exception
|
||||
*/
|
||||
public function options($uri, array $headers = [], $body = null): ResponseInterface;
|
||||
|
||||
/**
|
||||
* Sends a request with any HTTP method.
|
||||
*
|
||||
* @param string $method HTTP method to use
|
||||
* @param string|UriInterface $uri
|
||||
* @param string|StreamInterface|null $body
|
||||
*
|
||||
* @throws Exception
|
||||
*/
|
||||
public function send(string $method, $uri, array $headers = [], $body = null): ResponseInterface;
|
||||
}
|
||||
33
vendor/php-http/client-common/src/Plugin.php
vendored
Normal file
33
vendor/php-http/client-common/src/Plugin.php
vendored
Normal file
@@ -0,0 +1,33 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Http\Client\Common;
|
||||
|
||||
use Http\Promise\Promise;
|
||||
use Psr\Http\Message\RequestInterface;
|
||||
|
||||
/**
|
||||
* A plugin is a middleware to transform the request and/or the response.
|
||||
*
|
||||
* The plugin can:
|
||||
* - break the chain and return a response
|
||||
* - dispatch the request to the next middleware
|
||||
* - restart the request
|
||||
*
|
||||
* @author Joel Wurtz <joel.wurtz@gmail.com>
|
||||
*/
|
||||
interface Plugin
|
||||
{
|
||||
/**
|
||||
* Handle the request and return the response coming from the next callable.
|
||||
*
|
||||
* @see http://docs.php-http.org/en/latest/plugins/build-your-own.html
|
||||
*
|
||||
* @param callable(RequestInterface): Promise $next Next middleware in the chain, the request is passed as the first argument
|
||||
* @param callable(RequestInterface): Promise $first First middleware in the chain, used to to restart a request
|
||||
*
|
||||
* @return Promise Resolves a PSR-7 Response or fails with an Http\Client\Exception (The same as HttpAsyncClient)
|
||||
*/
|
||||
public function handleRequest(RequestInterface $request, callable $next, callable $first): Promise;
|
||||
}
|
||||
76
vendor/php-http/client-common/src/Plugin/AddHostPlugin.php
vendored
Normal file
76
vendor/php-http/client-common/src/Plugin/AddHostPlugin.php
vendored
Normal file
@@ -0,0 +1,76 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Http\Client\Common\Plugin;
|
||||
|
||||
use Http\Client\Common\Plugin;
|
||||
use Http\Promise\Promise;
|
||||
use Psr\Http\Message\RequestInterface;
|
||||
use Psr\Http\Message\UriInterface;
|
||||
use Symfony\Component\OptionsResolver\OptionsResolver;
|
||||
|
||||
/**
|
||||
* Add schema, host and port to a request. Can be set to overwrite the schema and host if desired.
|
||||
*
|
||||
* @author Tobias Nyholm <tobias.nyholm@gmail.com>
|
||||
*/
|
||||
final class AddHostPlugin implements Plugin
|
||||
{
|
||||
/**
|
||||
* @var UriInterface
|
||||
*/
|
||||
private $host;
|
||||
|
||||
/**
|
||||
* @var bool
|
||||
*/
|
||||
private $replace;
|
||||
|
||||
/**
|
||||
* @param array $config {
|
||||
*
|
||||
* @var bool $replace True will replace all hosts, false will only add host when none is specified.
|
||||
* }
|
||||
*/
|
||||
public function __construct(UriInterface $host, array $config = [])
|
||||
{
|
||||
if ('' === $host->getHost()) {
|
||||
throw new \LogicException('Host can not be empty');
|
||||
}
|
||||
|
||||
$this->host = $host;
|
||||
|
||||
$resolver = new OptionsResolver();
|
||||
$this->configureOptions($resolver);
|
||||
$options = $resolver->resolve($config);
|
||||
|
||||
$this->replace = $options['replace'];
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function handleRequest(RequestInterface $request, callable $next, callable $first): Promise
|
||||
{
|
||||
if ($this->replace || '' === $request->getUri()->getHost()) {
|
||||
$uri = $request->getUri()
|
||||
->withHost($this->host->getHost())
|
||||
->withScheme($this->host->getScheme())
|
||||
->withPort($this->host->getPort())
|
||||
;
|
||||
|
||||
$request = $request->withUri($uri);
|
||||
}
|
||||
|
||||
return $next($request);
|
||||
}
|
||||
|
||||
private function configureOptions(OptionsResolver $resolver): void
|
||||
{
|
||||
$resolver->setDefaults([
|
||||
'replace' => false,
|
||||
]);
|
||||
$resolver->setAllowedTypes('replace', 'bool');
|
||||
}
|
||||
}
|
||||
78
vendor/php-http/client-common/src/Plugin/AddPathPlugin.php
vendored
Normal file
78
vendor/php-http/client-common/src/Plugin/AddPathPlugin.php
vendored
Normal file
@@ -0,0 +1,78 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Http\Client\Common\Plugin;
|
||||
|
||||
use Http\Client\Common\Plugin;
|
||||
use Http\Promise\Promise;
|
||||
use Psr\Http\Message\RequestInterface;
|
||||
use Psr\Http\Message\UriInterface;
|
||||
|
||||
/**
|
||||
* Prepend a base path to the request URI. Useful for base API URLs like http://domain.com/api.
|
||||
*
|
||||
* @author Sullivan Senechal <soullivaneuh@gmail.com>
|
||||
*/
|
||||
final class AddPathPlugin implements Plugin
|
||||
{
|
||||
/**
|
||||
* @var UriInterface
|
||||
*/
|
||||
private $uri;
|
||||
|
||||
public function __construct(UriInterface $uri)
|
||||
{
|
||||
if ('' === $uri->getPath()) {
|
||||
throw new \LogicException('URI path cannot be empty');
|
||||
}
|
||||
|
||||
if ('/' === substr($uri->getPath(), -1)) {
|
||||
$uri = $uri->withPath(rtrim($uri->getPath(), '/'));
|
||||
}
|
||||
|
||||
$this->uri = $uri;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a prefix in the beginning of the URL's path.
|
||||
*
|
||||
* The prefix is not added if that prefix is already on the URL's path. This will fail on the edge
|
||||
* case of the prefix being repeated, for example if `https://example.com/api/api/foo` is a valid
|
||||
* URL on the server and the configured prefix is `/api`.
|
||||
*
|
||||
* We looked at other solutions, but they are all much more complicated, while still having edge
|
||||
* cases:
|
||||
* - Doing an spl_object_hash on `$first` will lead to collisions over time because over time the
|
||||
* hash can collide.
|
||||
* - Have the PluginClient provide a magic header to identify the request chain and only apply
|
||||
* this plugin once.
|
||||
*
|
||||
* There are 2 reasons for the AddPathPlugin to be executed twice on the same request:
|
||||
* - A plugin can restart the chain by calling `$first`, e.g. redirect
|
||||
* - A plugin can call `$next` more than once, e.g. retry
|
||||
*
|
||||
* Depending on the scenario, the path should or should not be added. E.g. `$first` could
|
||||
* be called after a redirect response from the server. The server likely already has the
|
||||
* correct path.
|
||||
*
|
||||
* No solution fits all use cases. This implementation will work fine for the common use cases.
|
||||
* If you have a specific situation where this is not the right thing, you can build a custom plugin
|
||||
* that does exactly what you need.
|
||||
*
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function handleRequest(RequestInterface $request, callable $next, callable $first): Promise
|
||||
{
|
||||
$prepend = $this->uri->getPath();
|
||||
$path = $request->getUri()->getPath();
|
||||
|
||||
if (substr($path, 0, strlen($prepend)) !== $prepend) {
|
||||
$request = $request->withUri($request->getUri()
|
||||
->withPath($prepend.$path)
|
||||
);
|
||||
}
|
||||
|
||||
return $next($request);
|
||||
}
|
||||
}
|
||||
38
vendor/php-http/client-common/src/Plugin/AuthenticationPlugin.php
vendored
Normal file
38
vendor/php-http/client-common/src/Plugin/AuthenticationPlugin.php
vendored
Normal file
@@ -0,0 +1,38 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Http\Client\Common\Plugin;
|
||||
|
||||
use Http\Client\Common\Plugin;
|
||||
use Http\Message\Authentication;
|
||||
use Http\Promise\Promise;
|
||||
use Psr\Http\Message\RequestInterface;
|
||||
|
||||
/**
|
||||
* Send an authenticated request.
|
||||
*
|
||||
* @author Joel Wurtz <joel.wurtz@gmail.com>
|
||||
*/
|
||||
final class AuthenticationPlugin implements Plugin
|
||||
{
|
||||
/**
|
||||
* @var Authentication An authentication system
|
||||
*/
|
||||
private $authentication;
|
||||
|
||||
public function __construct(Authentication $authentication)
|
||||
{
|
||||
$this->authentication = $authentication;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function handleRequest(RequestInterface $request, callable $next, callable $first): Promise
|
||||
{
|
||||
$request = $this->authentication->authenticate($request);
|
||||
|
||||
return $next($request);
|
||||
}
|
||||
}
|
||||
57
vendor/php-http/client-common/src/Plugin/BaseUriPlugin.php
vendored
Normal file
57
vendor/php-http/client-common/src/Plugin/BaseUriPlugin.php
vendored
Normal file
@@ -0,0 +1,57 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Http\Client\Common\Plugin;
|
||||
|
||||
use Http\Client\Common\Plugin;
|
||||
use Http\Promise\Promise;
|
||||
use Psr\Http\Message\RequestInterface;
|
||||
use Psr\Http\Message\UriInterface;
|
||||
|
||||
/**
|
||||
* Combines the AddHostPlugin and AddPathPlugin.
|
||||
*
|
||||
* @author Sullivan Senechal <soullivaneuh@gmail.com>
|
||||
*/
|
||||
final class BaseUriPlugin implements Plugin
|
||||
{
|
||||
/**
|
||||
* @var AddHostPlugin
|
||||
*/
|
||||
private $addHostPlugin;
|
||||
|
||||
/**
|
||||
* @var AddPathPlugin|null
|
||||
*/
|
||||
private $addPathPlugin = null;
|
||||
|
||||
/**
|
||||
* @param UriInterface $uri Has to contain a host name and can have a path
|
||||
* @param array $hostConfig Config for AddHostPlugin. @see AddHostPlugin::configureOptions
|
||||
*/
|
||||
public function __construct(UriInterface $uri, array $hostConfig = [])
|
||||
{
|
||||
$this->addHostPlugin = new AddHostPlugin($uri, $hostConfig);
|
||||
|
||||
if (rtrim($uri->getPath(), '/')) {
|
||||
$this->addPathPlugin = new AddPathPlugin($uri);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function handleRequest(RequestInterface $request, callable $next, callable $first): Promise
|
||||
{
|
||||
$addHostNext = function (RequestInterface $request) use ($next, $first) {
|
||||
return $this->addHostPlugin->handleRequest($request, $next, $first);
|
||||
};
|
||||
|
||||
if ($this->addPathPlugin) {
|
||||
return $this->addPathPlugin->handleRequest($request, $addHostNext, $first);
|
||||
}
|
||||
|
||||
return $addHostNext($request);
|
||||
}
|
||||
}
|
||||
39
vendor/php-http/client-common/src/Plugin/ContentLengthPlugin.php
vendored
Normal file
39
vendor/php-http/client-common/src/Plugin/ContentLengthPlugin.php
vendored
Normal file
@@ -0,0 +1,39 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Http\Client\Common\Plugin;
|
||||
|
||||
use Http\Client\Common\Plugin;
|
||||
use Http\Message\Encoding\ChunkStream;
|
||||
use Http\Promise\Promise;
|
||||
use Psr\Http\Message\RequestInterface;
|
||||
|
||||
/**
|
||||
* Allow to set the correct content length header on the request or to transfer it as a chunk if not possible.
|
||||
*
|
||||
* @author Joel Wurtz <joel.wurtz@gmail.com>
|
||||
*/
|
||||
final class ContentLengthPlugin implements Plugin
|
||||
{
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function handleRequest(RequestInterface $request, callable $next, callable $first): Promise
|
||||
{
|
||||
if (!$request->hasHeader('Content-Length')) {
|
||||
$stream = $request->getBody();
|
||||
|
||||
// Cannot determine the size so we use a chunk stream
|
||||
if (null === $stream->getSize()) {
|
||||
$stream = new ChunkStream($stream);
|
||||
$request = $request->withBody($stream);
|
||||
$request = $request->withAddedHeader('Transfer-Encoding', 'chunked');
|
||||
} else {
|
||||
$request = $request->withHeader('Content-Length', (string) $stream->getSize());
|
||||
}
|
||||
}
|
||||
|
||||
return $next($request);
|
||||
}
|
||||
}
|
||||
122
vendor/php-http/client-common/src/Plugin/ContentTypePlugin.php
vendored
Normal file
122
vendor/php-http/client-common/src/Plugin/ContentTypePlugin.php
vendored
Normal file
@@ -0,0 +1,122 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Http\Client\Common\Plugin;
|
||||
|
||||
use Http\Client\Common\Plugin;
|
||||
use Http\Promise\Promise;
|
||||
use Psr\Http\Message\RequestInterface;
|
||||
use Psr\Http\Message\StreamInterface;
|
||||
use Symfony\Component\OptionsResolver\OptionsResolver;
|
||||
|
||||
/**
|
||||
* Allow to set the correct content type header on the request automatically only if it is not set.
|
||||
*
|
||||
* @author Karim Pinchon <karim.pinchon@gmail.com>
|
||||
*/
|
||||
final class ContentTypePlugin implements Plugin
|
||||
{
|
||||
/**
|
||||
* Allow to disable the content type detection when stream is too large (as it can consume a lot of resource).
|
||||
*
|
||||
* @var bool
|
||||
*
|
||||
* true skip the content type detection
|
||||
* false detect the content type (default value)
|
||||
*/
|
||||
private $skipDetection;
|
||||
|
||||
/**
|
||||
* Determine the size stream limit for which the detection as to be skipped (default to 16Mb).
|
||||
*
|
||||
* @var int
|
||||
*/
|
||||
private $sizeLimit;
|
||||
|
||||
/**
|
||||
* @param array $config {
|
||||
*
|
||||
* @var bool $skip_detection true skip detection if stream size is bigger than $size_limit
|
||||
* @var int $size_limit size stream limit for which the detection as to be skipped.
|
||||
* }
|
||||
*/
|
||||
public function __construct(array $config = [])
|
||||
{
|
||||
$resolver = new OptionsResolver();
|
||||
$resolver->setDefaults([
|
||||
'skip_detection' => false,
|
||||
'size_limit' => 16000000,
|
||||
]);
|
||||
$resolver->setAllowedTypes('skip_detection', 'bool');
|
||||
$resolver->setAllowedTypes('size_limit', 'int');
|
||||
|
||||
$options = $resolver->resolve($config);
|
||||
|
||||
$this->skipDetection = $options['skip_detection'];
|
||||
$this->sizeLimit = $options['size_limit'];
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function handleRequest(RequestInterface $request, callable $next, callable $first): Promise
|
||||
{
|
||||
if (!$request->hasHeader('Content-Type')) {
|
||||
$stream = $request->getBody();
|
||||
$streamSize = $stream->getSize();
|
||||
|
||||
if (!$stream->isSeekable()) {
|
||||
return $next($request);
|
||||
}
|
||||
|
||||
if (0 === $streamSize) {
|
||||
return $next($request);
|
||||
}
|
||||
|
||||
if ($this->skipDetection && (null === $streamSize || $streamSize >= $this->sizeLimit)) {
|
||||
return $next($request);
|
||||
}
|
||||
|
||||
if ($this->isJson($stream)) {
|
||||
$request = $request->withHeader('Content-Type', 'application/json');
|
||||
|
||||
return $next($request);
|
||||
}
|
||||
|
||||
if ($this->isXml($stream)) {
|
||||
$request = $request->withHeader('Content-Type', 'application/xml');
|
||||
|
||||
return $next($request);
|
||||
}
|
||||
}
|
||||
|
||||
return $next($request);
|
||||
}
|
||||
|
||||
private function isJson(StreamInterface $stream): bool
|
||||
{
|
||||
if (!function_exists('json_decode')) {
|
||||
return false;
|
||||
}
|
||||
$stream->rewind();
|
||||
|
||||
json_decode($stream->getContents());
|
||||
|
||||
return JSON_ERROR_NONE === json_last_error();
|
||||
}
|
||||
|
||||
private function isXml(StreamInterface $stream): bool
|
||||
{
|
||||
if (!function_exists('simplexml_load_string')) {
|
||||
return false;
|
||||
}
|
||||
$stream->rewind();
|
||||
|
||||
$previousValue = libxml_use_internal_errors(true);
|
||||
$isXml = simplexml_load_string($stream->getContents());
|
||||
libxml_use_internal_errors($previousValue);
|
||||
|
||||
return false !== $isXml;
|
||||
}
|
||||
}
|
||||
180
vendor/php-http/client-common/src/Plugin/CookiePlugin.php
vendored
Normal file
180
vendor/php-http/client-common/src/Plugin/CookiePlugin.php
vendored
Normal file
@@ -0,0 +1,180 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Http\Client\Common\Plugin;
|
||||
|
||||
use Http\Client\Common\Plugin;
|
||||
use Http\Client\Exception\TransferException;
|
||||
use Http\Message\Cookie;
|
||||
use Http\Message\CookieJar;
|
||||
use Http\Message\CookieUtil;
|
||||
use Http\Message\Exception\UnexpectedValueException;
|
||||
use Http\Promise\Promise;
|
||||
use Psr\Http\Message\RequestInterface;
|
||||
use Psr\Http\Message\ResponseInterface;
|
||||
|
||||
/**
|
||||
* Handle request cookies.
|
||||
*
|
||||
* @author Joel Wurtz <joel.wurtz@gmail.com>
|
||||
*/
|
||||
final class CookiePlugin implements Plugin
|
||||
{
|
||||
/**
|
||||
* Cookie storage.
|
||||
*
|
||||
* @var CookieJar
|
||||
*/
|
||||
private $cookieJar;
|
||||
|
||||
public function __construct(CookieJar $cookieJar)
|
||||
{
|
||||
$this->cookieJar = $cookieJar;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function handleRequest(RequestInterface $request, callable $next, callable $first): Promise
|
||||
{
|
||||
$cookies = [];
|
||||
foreach ($this->cookieJar->getCookies() as $cookie) {
|
||||
if ($cookie->isExpired()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!$cookie->matchDomain($request->getUri()->getHost())) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!$cookie->matchPath($request->getUri()->getPath())) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if ($cookie->isSecure() && ('https' !== $request->getUri()->getScheme())) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$cookies[] = sprintf('%s=%s', $cookie->getName(), $cookie->getValue());
|
||||
}
|
||||
|
||||
if (!empty($cookies)) {
|
||||
$request = $request->withAddedHeader('Cookie', implode('; ', array_unique($cookies)));
|
||||
}
|
||||
|
||||
return $next($request)->then(function (ResponseInterface $response) use ($request) {
|
||||
if ($response->hasHeader('Set-Cookie')) {
|
||||
$setCookies = $response->getHeader('Set-Cookie');
|
||||
|
||||
foreach ($setCookies as $setCookie) {
|
||||
$cookie = $this->createCookie($request, $setCookie);
|
||||
|
||||
// Cookie invalid do not use it
|
||||
if (null === $cookie) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Restrict setting cookie from another domain
|
||||
if (!preg_match("/\.{$cookie->getDomain()}$/", '.'.$request->getUri()->getHost())) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$this->cookieJar->addCookie($cookie);
|
||||
}
|
||||
}
|
||||
|
||||
return $response;
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a cookie from a string.
|
||||
*
|
||||
* @throws TransferException
|
||||
*/
|
||||
private function createCookie(RequestInterface $request, string $setCookieHeader): ?Cookie
|
||||
{
|
||||
$parts = array_map('trim', explode(';', $setCookieHeader));
|
||||
|
||||
if (empty($parts) || !strpos($parts[0], '=')) {
|
||||
return null;
|
||||
}
|
||||
|
||||
list($name, $cookieValue) = $this->createValueKey(array_shift($parts));
|
||||
|
||||
$maxAge = null;
|
||||
$expires = null;
|
||||
$domain = $request->getUri()->getHost();
|
||||
$path = $request->getUri()->getPath();
|
||||
$secure = false;
|
||||
$httpOnly = false;
|
||||
|
||||
// Add the cookie pieces into the parsed data array
|
||||
foreach ($parts as $part) {
|
||||
list($key, $value) = $this->createValueKey($part);
|
||||
|
||||
switch (strtolower($key)) {
|
||||
case 'expires':
|
||||
try {
|
||||
$expires = CookieUtil::parseDate((string) $value);
|
||||
} catch (UnexpectedValueException $e) {
|
||||
throw new TransferException(
|
||||
sprintf(
|
||||
'Cookie header `%s` expires value `%s` could not be converted to date',
|
||||
$name,
|
||||
$value
|
||||
),
|
||||
0,
|
||||
$e
|
||||
);
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
case 'max-age':
|
||||
$maxAge = (int) $value;
|
||||
|
||||
break;
|
||||
|
||||
case 'domain':
|
||||
$domain = $value;
|
||||
|
||||
break;
|
||||
|
||||
case 'path':
|
||||
$path = $value;
|
||||
|
||||
break;
|
||||
|
||||
case 'secure':
|
||||
$secure = true;
|
||||
|
||||
break;
|
||||
|
||||
case 'httponly':
|
||||
$httpOnly = true;
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return new Cookie($name, $cookieValue, $maxAge, $domain, $path, $secure, $httpOnly, $expires);
|
||||
}
|
||||
|
||||
/**
|
||||
* Separates key/value pair from cookie.
|
||||
*
|
||||
* @param string $part A single cookie value in format key=value
|
||||
*
|
||||
* @return array{0:string, 1:?string}
|
||||
*/
|
||||
private function createValueKey(string $part): array
|
||||
{
|
||||
$parts = explode('=', $part, 2);
|
||||
$key = trim($parts[0]);
|
||||
$value = isset($parts[1]) ? trim($parts[1]) : null;
|
||||
|
||||
return [$key, $value];
|
||||
}
|
||||
}
|
||||
135
vendor/php-http/client-common/src/Plugin/DecoderPlugin.php
vendored
Normal file
135
vendor/php-http/client-common/src/Plugin/DecoderPlugin.php
vendored
Normal file
@@ -0,0 +1,135 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Http\Client\Common\Plugin;
|
||||
|
||||
use Http\Client\Common\Plugin;
|
||||
use Http\Message\Encoding;
|
||||
use Http\Promise\Promise;
|
||||
use Psr\Http\Message\RequestInterface;
|
||||
use Psr\Http\Message\ResponseInterface;
|
||||
use Psr\Http\Message\StreamInterface;
|
||||
use Symfony\Component\OptionsResolver\OptionsResolver;
|
||||
|
||||
/**
|
||||
* Allow to decode response body with a chunk, deflate, compress or gzip encoding.
|
||||
*
|
||||
* If zlib is not installed, only chunked encoding can be handled.
|
||||
*
|
||||
* If Content-Encoding is not disabled, the plugin will add an Accept-Encoding header for the encoding methods it supports.
|
||||
*
|
||||
* @author Joel Wurtz <joel.wurtz@gmail.com>
|
||||
*/
|
||||
final class DecoderPlugin implements Plugin
|
||||
{
|
||||
/**
|
||||
* @var bool Whether this plugin decode stream with value in the Content-Encoding header (default to true).
|
||||
*
|
||||
* If set to false only the Transfer-Encoding header will be used
|
||||
*/
|
||||
private $useContentEncoding;
|
||||
|
||||
/**
|
||||
* @param array $config {
|
||||
*
|
||||
* @var bool $use_content_encoding Whether this plugin should look at the Content-Encoding header first or only at the Transfer-Encoding (defaults to true).
|
||||
* }
|
||||
*/
|
||||
public function __construct(array $config = [])
|
||||
{
|
||||
$resolver = new OptionsResolver();
|
||||
$resolver->setDefaults([
|
||||
'use_content_encoding' => true,
|
||||
]);
|
||||
$resolver->setAllowedTypes('use_content_encoding', 'bool');
|
||||
$options = $resolver->resolve($config);
|
||||
|
||||
$this->useContentEncoding = $options['use_content_encoding'];
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function handleRequest(RequestInterface $request, callable $next, callable $first): Promise
|
||||
{
|
||||
$encodings = extension_loaded('zlib') ? ['gzip', 'deflate'] : ['identity'];
|
||||
|
||||
if ($this->useContentEncoding) {
|
||||
$request = $request->withHeader('Accept-Encoding', $encodings);
|
||||
}
|
||||
$encodings[] = 'chunked';
|
||||
$request = $request->withHeader('TE', $encodings);
|
||||
|
||||
return $next($request)->then(function (ResponseInterface $response) {
|
||||
return $this->decodeResponse($response);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Decode a response body given its Transfer-Encoding or Content-Encoding value.
|
||||
*/
|
||||
private function decodeResponse(ResponseInterface $response): ResponseInterface
|
||||
{
|
||||
$response = $this->decodeOnEncodingHeader('Transfer-Encoding', $response);
|
||||
|
||||
if ($this->useContentEncoding) {
|
||||
$response = $this->decodeOnEncodingHeader('Content-Encoding', $response);
|
||||
}
|
||||
|
||||
return $response;
|
||||
}
|
||||
|
||||
/**
|
||||
* Decode a response on a specific header (content encoding or transfer encoding mainly).
|
||||
*/
|
||||
private function decodeOnEncodingHeader(string $headerName, ResponseInterface $response): ResponseInterface
|
||||
{
|
||||
if ($response->hasHeader($headerName)) {
|
||||
$encodings = $response->getHeader($headerName);
|
||||
$newEncodings = [];
|
||||
|
||||
while ($encoding = array_pop($encodings)) {
|
||||
$stream = $this->decorateStream($encoding, $response->getBody());
|
||||
|
||||
if (false === $stream) {
|
||||
array_unshift($newEncodings, $encoding);
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
$response = $response->withBody($stream);
|
||||
}
|
||||
|
||||
if (\count($newEncodings) > 0) {
|
||||
$response = $response->withHeader($headerName, $newEncodings);
|
||||
} else {
|
||||
$response = $response->withoutHeader($headerName);
|
||||
}
|
||||
}
|
||||
|
||||
return $response;
|
||||
}
|
||||
|
||||
/**
|
||||
* Decorate a stream given an encoding.
|
||||
*
|
||||
* @return StreamInterface|false A new stream interface or false if encoding is not supported
|
||||
*/
|
||||
private function decorateStream(string $encoding, StreamInterface $stream)
|
||||
{
|
||||
if ('chunked' === strtolower($encoding)) {
|
||||
return new Encoding\DechunkStream($stream);
|
||||
}
|
||||
|
||||
if ('deflate' === strtolower($encoding)) {
|
||||
return new Encoding\DecompressStream($stream);
|
||||
}
|
||||
|
||||
if ('gzip' === strtolower($encoding)) {
|
||||
return new Encoding\GzipDecodeStream($stream);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
92
vendor/php-http/client-common/src/Plugin/ErrorPlugin.php
vendored
Normal file
92
vendor/php-http/client-common/src/Plugin/ErrorPlugin.php
vendored
Normal file
@@ -0,0 +1,92 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Http\Client\Common\Plugin;
|
||||
|
||||
use Http\Client\Common\Exception\ClientErrorException;
|
||||
use Http\Client\Common\Exception\ServerErrorException;
|
||||
use Http\Client\Common\Plugin;
|
||||
use Http\Promise\Promise;
|
||||
use Psr\Http\Message\RequestInterface;
|
||||
use Psr\Http\Message\ResponseInterface;
|
||||
use Symfony\Component\OptionsResolver\OptionsResolver;
|
||||
|
||||
/**
|
||||
* Throw exception when the response of a request is not acceptable.
|
||||
*
|
||||
* Status codes 400-499 lead to a ClientErrorException, status 500-599 to a ServerErrorException.
|
||||
*
|
||||
* Warning
|
||||
* =======
|
||||
*
|
||||
* Throwing an exception on a valid response violates the PSR-18 specification.
|
||||
* This plugin is provided as a convenience when writing a small application.
|
||||
* When providing a client to a third party library, this plugin must not be
|
||||
* included, or the third party library will have problems with error handling.
|
||||
*
|
||||
* @author Joel Wurtz <joel.wurtz@gmail.com>
|
||||
*/
|
||||
final class ErrorPlugin implements Plugin
|
||||
{
|
||||
/**
|
||||
* @var bool Whether this plugin should only throw 5XX Exceptions (default to false).
|
||||
*
|
||||
* If set to true 4XX Responses code will never throw an exception
|
||||
*/
|
||||
private $onlyServerException;
|
||||
|
||||
/**
|
||||
* @param array $config {
|
||||
*
|
||||
* @var bool only_server_exception Whether this plugin should only throw 5XX Exceptions (default to false).
|
||||
* }
|
||||
*/
|
||||
public function __construct(array $config = [])
|
||||
{
|
||||
$resolver = new OptionsResolver();
|
||||
$resolver->setDefaults([
|
||||
'only_server_exception' => false,
|
||||
]);
|
||||
$resolver->setAllowedTypes('only_server_exception', 'bool');
|
||||
$options = $resolver->resolve($config);
|
||||
|
||||
$this->onlyServerException = $options['only_server_exception'];
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function handleRequest(RequestInterface $request, callable $next, callable $first): Promise
|
||||
{
|
||||
$promise = $next($request);
|
||||
|
||||
return $promise->then(function (ResponseInterface $response) use ($request) {
|
||||
return $this->transformResponseToException($request, $response);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Transform response to an error if possible.
|
||||
*
|
||||
* @param RequestInterface $request Request of the call
|
||||
* @param ResponseInterface $response Response of the call
|
||||
*
|
||||
* @throws ClientErrorException If response status code is a 4xx
|
||||
* @throws ServerErrorException If response status code is a 5xx
|
||||
*
|
||||
* @return ResponseInterface If status code is not in 4xx or 5xx return response
|
||||
*/
|
||||
private function transformResponseToException(RequestInterface $request, ResponseInterface $response): ResponseInterface
|
||||
{
|
||||
if (!$this->onlyServerException && $response->getStatusCode() >= 400 && $response->getStatusCode() < 500) {
|
||||
throw new ClientErrorException($response->getReasonPhrase(), $request, $response);
|
||||
}
|
||||
|
||||
if ($response->getStatusCode() >= 500 && $response->getStatusCode() < 600) {
|
||||
throw new ServerErrorException($response->getReasonPhrase(), $request, $response);
|
||||
}
|
||||
|
||||
return $response;
|
||||
}
|
||||
}
|
||||
48
vendor/php-http/client-common/src/Plugin/HeaderAppendPlugin.php
vendored
Normal file
48
vendor/php-http/client-common/src/Plugin/HeaderAppendPlugin.php
vendored
Normal file
@@ -0,0 +1,48 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Http\Client\Common\Plugin;
|
||||
|
||||
use Http\Client\Common\Plugin;
|
||||
use Http\Promise\Promise;
|
||||
use Psr\Http\Message\RequestInterface;
|
||||
|
||||
/**
|
||||
* Append headers to the request.
|
||||
*
|
||||
* If the header already exists the value will be appended to the current value.
|
||||
*
|
||||
* This only makes sense for headers that can have multiple values like 'Forwarded'
|
||||
*
|
||||
* @see https://en.wikipedia.org/wiki/List_of_HTTP_header_fields
|
||||
*
|
||||
* @author Soufiane Ghzal <sghzal@gmail.com>
|
||||
*/
|
||||
final class HeaderAppendPlugin implements Plugin
|
||||
{
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
private $headers;
|
||||
|
||||
/**
|
||||
* @param array $headers Hashmap of header name to header value
|
||||
*/
|
||||
public function __construct(array $headers)
|
||||
{
|
||||
$this->headers = $headers;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function handleRequest(RequestInterface $request, callable $next, callable $first): Promise
|
||||
{
|
||||
foreach ($this->headers as $header => $headerValue) {
|
||||
$request = $request->withAddedHeader($header, $headerValue);
|
||||
}
|
||||
|
||||
return $next($request);
|
||||
}
|
||||
}
|
||||
46
vendor/php-http/client-common/src/Plugin/HeaderDefaultsPlugin.php
vendored
Normal file
46
vendor/php-http/client-common/src/Plugin/HeaderDefaultsPlugin.php
vendored
Normal file
@@ -0,0 +1,46 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Http\Client\Common\Plugin;
|
||||
|
||||
use Http\Client\Common\Plugin;
|
||||
use Http\Promise\Promise;
|
||||
use Psr\Http\Message\RequestInterface;
|
||||
|
||||
/**
|
||||
* Set header to default value if it does not exist.
|
||||
*
|
||||
* If a given header already exists the value wont be replaced and the request wont be changed.
|
||||
*
|
||||
* @author Soufiane Ghzal <sghzal@gmail.com>
|
||||
*/
|
||||
final class HeaderDefaultsPlugin implements Plugin
|
||||
{
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
private $headers = [];
|
||||
|
||||
/**
|
||||
* @param array $headers Hashmap of header name to header value
|
||||
*/
|
||||
public function __construct(array $headers)
|
||||
{
|
||||
$this->headers = $headers;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function handleRequest(RequestInterface $request, callable $next, callable $first): Promise
|
||||
{
|
||||
foreach ($this->headers as $header => $headerValue) {
|
||||
if (!$request->hasHeader($header)) {
|
||||
$request = $request->withHeader($header, $headerValue);
|
||||
}
|
||||
}
|
||||
|
||||
return $next($request);
|
||||
}
|
||||
}
|
||||
44
vendor/php-http/client-common/src/Plugin/HeaderRemovePlugin.php
vendored
Normal file
44
vendor/php-http/client-common/src/Plugin/HeaderRemovePlugin.php
vendored
Normal file
@@ -0,0 +1,44 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Http\Client\Common\Plugin;
|
||||
|
||||
use Http\Client\Common\Plugin;
|
||||
use Http\Promise\Promise;
|
||||
use Psr\Http\Message\RequestInterface;
|
||||
|
||||
/**
|
||||
* Removes headers from the request.
|
||||
*
|
||||
* @author Soufiane Ghzal <sghzal@gmail.com>
|
||||
*/
|
||||
final class HeaderRemovePlugin implements Plugin
|
||||
{
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
private $headers = [];
|
||||
|
||||
/**
|
||||
* @param array $headers List of header names to remove from the request
|
||||
*/
|
||||
public function __construct(array $headers)
|
||||
{
|
||||
$this->headers = $headers;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function handleRequest(RequestInterface $request, callable $next, callable $first): Promise
|
||||
{
|
||||
foreach ($this->headers as $header) {
|
||||
if ($request->hasHeader($header)) {
|
||||
$request = $request->withoutHeader($header);
|
||||
}
|
||||
}
|
||||
|
||||
return $next($request);
|
||||
}
|
||||
}
|
||||
44
vendor/php-http/client-common/src/Plugin/HeaderSetPlugin.php
vendored
Normal file
44
vendor/php-http/client-common/src/Plugin/HeaderSetPlugin.php
vendored
Normal file
@@ -0,0 +1,44 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Http\Client\Common\Plugin;
|
||||
|
||||
use Http\Client\Common\Plugin;
|
||||
use Http\Promise\Promise;
|
||||
use Psr\Http\Message\RequestInterface;
|
||||
|
||||
/**
|
||||
* Set headers on the request.
|
||||
*
|
||||
* If the header does not exist it wil be set, if the header already exists it will be replaced.
|
||||
*
|
||||
* @author Soufiane Ghzal <sghzal@gmail.com>
|
||||
*/
|
||||
final class HeaderSetPlugin implements Plugin
|
||||
{
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
private $headers;
|
||||
|
||||
/**
|
||||
* @param array $headers Hashmap of header name to header value
|
||||
*/
|
||||
public function __construct(array $headers)
|
||||
{
|
||||
$this->headers = $headers;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function handleRequest(RequestInterface $request, callable $next, callable $first): Promise
|
||||
{
|
||||
foreach ($this->headers as $header => $headerValue) {
|
||||
$request = $request->withHeader($header, $headerValue);
|
||||
}
|
||||
|
||||
return $next($request);
|
||||
}
|
||||
}
|
||||
49
vendor/php-http/client-common/src/Plugin/HistoryPlugin.php
vendored
Normal file
49
vendor/php-http/client-common/src/Plugin/HistoryPlugin.php
vendored
Normal file
@@ -0,0 +1,49 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Http\Client\Common\Plugin;
|
||||
|
||||
use Http\Client\Common\Plugin;
|
||||
use Http\Promise\Promise;
|
||||
use Psr\Http\Client\ClientExceptionInterface;
|
||||
use Psr\Http\Message\RequestInterface;
|
||||
use Psr\Http\Message\ResponseInterface;
|
||||
|
||||
/**
|
||||
* Record HTTP calls.
|
||||
*
|
||||
* @author Joel Wurtz <joel.wurtz@gmail.com>
|
||||
*/
|
||||
final class HistoryPlugin implements Plugin
|
||||
{
|
||||
/**
|
||||
* Journal use to store request / responses / exception.
|
||||
*
|
||||
* @var Journal
|
||||
*/
|
||||
private $journal;
|
||||
|
||||
public function __construct(Journal $journal)
|
||||
{
|
||||
$this->journal = $journal;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function handleRequest(RequestInterface $request, callable $next, callable $first): Promise
|
||||
{
|
||||
$journal = $this->journal;
|
||||
|
||||
return $next($request)->then(function (ResponseInterface $response) use ($request, $journal) {
|
||||
$journal->addSuccess($request, $response);
|
||||
|
||||
return $response;
|
||||
}, function (ClientExceptionInterface $exception) use ($request, $journal) {
|
||||
$journal->addFailure($request, $exception);
|
||||
|
||||
throw $exception;
|
||||
});
|
||||
}
|
||||
}
|
||||
33
vendor/php-http/client-common/src/Plugin/Journal.php
vendored
Normal file
33
vendor/php-http/client-common/src/Plugin/Journal.php
vendored
Normal file
@@ -0,0 +1,33 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Http\Client\Common\Plugin;
|
||||
|
||||
use Psr\Http\Client\ClientExceptionInterface;
|
||||
use Psr\Http\Message\RequestInterface;
|
||||
use Psr\Http\Message\ResponseInterface;
|
||||
|
||||
/**
|
||||
* Records history of HTTP calls.
|
||||
*
|
||||
* @author Joel Wurtz <joel.wurtz@gmail.com>
|
||||
*/
|
||||
interface Journal
|
||||
{
|
||||
/**
|
||||
* Record a successful call.
|
||||
*
|
||||
* @param RequestInterface $request Request use to make the call
|
||||
* @param ResponseInterface $response Response returned by the call
|
||||
*/
|
||||
public function addSuccess(RequestInterface $request, ResponseInterface $response);
|
||||
|
||||
/**
|
||||
* Record a failed call.
|
||||
*
|
||||
* @param RequestInterface $request Request use to make the call
|
||||
* @param ClientExceptionInterface $exception Exception returned by the call
|
||||
*/
|
||||
public function addFailure(RequestInterface $request, ClientExceptionInterface $exception);
|
||||
}
|
||||
50
vendor/php-http/client-common/src/Plugin/QueryDefaultsPlugin.php
vendored
Normal file
50
vendor/php-http/client-common/src/Plugin/QueryDefaultsPlugin.php
vendored
Normal file
@@ -0,0 +1,50 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Http\Client\Common\Plugin;
|
||||
|
||||
use Http\Client\Common\Plugin;
|
||||
use Http\Promise\Promise;
|
||||
use Psr\Http\Message\RequestInterface;
|
||||
|
||||
/**
|
||||
* Set query to default value if it does not exist.
|
||||
*
|
||||
* If a given query parameter already exists the value wont be replaced and the request wont be changed.
|
||||
*
|
||||
* @author Tobias Nyholm <tobias.nyholm@gmail.com>
|
||||
*/
|
||||
final class QueryDefaultsPlugin implements Plugin
|
||||
{
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
private $queryParams = [];
|
||||
|
||||
/**
|
||||
* @param array $queryParams Hashmap of query name to query value. Names and values must not be url encoded as
|
||||
* this plugin will encode them
|
||||
*/
|
||||
public function __construct(array $queryParams)
|
||||
{
|
||||
$this->queryParams = $queryParams;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function handleRequest(RequestInterface $request, callable $next, callable $first): Promise
|
||||
{
|
||||
$uri = $request->getUri();
|
||||
|
||||
parse_str($uri->getQuery(), $query);
|
||||
$query += $this->queryParams;
|
||||
|
||||
$request = $request->withUri(
|
||||
$uri->withQuery(http_build_query($query))
|
||||
);
|
||||
|
||||
return $next($request);
|
||||
}
|
||||
}
|
||||
258
vendor/php-http/client-common/src/Plugin/RedirectPlugin.php
vendored
Normal file
258
vendor/php-http/client-common/src/Plugin/RedirectPlugin.php
vendored
Normal file
@@ -0,0 +1,258 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Http\Client\Common\Plugin;
|
||||
|
||||
use Http\Client\Common\Exception\CircularRedirectionException;
|
||||
use Http\Client\Common\Exception\MultipleRedirectionException;
|
||||
use Http\Client\Common\Plugin;
|
||||
use Http\Client\Exception\HttpException;
|
||||
use Http\Promise\Promise;
|
||||
use Psr\Http\Message\RequestInterface;
|
||||
use Psr\Http\Message\ResponseInterface;
|
||||
use Psr\Http\Message\UriInterface;
|
||||
use Symfony\Component\OptionsResolver\OptionsResolver;
|
||||
|
||||
/**
|
||||
* Follow redirections.
|
||||
*
|
||||
* @author Joel Wurtz <joel.wurtz@gmail.com>
|
||||
*/
|
||||
final class RedirectPlugin implements Plugin
|
||||
{
|
||||
/**
|
||||
* Rule on how to redirect, change method for the new request.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
private $redirectCodes = [
|
||||
300 => [
|
||||
'switch' => [
|
||||
'unless' => ['GET', 'HEAD'],
|
||||
'to' => 'GET',
|
||||
],
|
||||
'multiple' => true,
|
||||
'permanent' => false,
|
||||
],
|
||||
301 => [
|
||||
'switch' => [
|
||||
'unless' => ['GET', 'HEAD'],
|
||||
'to' => 'GET',
|
||||
],
|
||||
'multiple' => false,
|
||||
'permanent' => true,
|
||||
],
|
||||
302 => [
|
||||
'switch' => [
|
||||
'unless' => ['GET', 'HEAD'],
|
||||
'to' => 'GET',
|
||||
],
|
||||
'multiple' => false,
|
||||
'permanent' => false,
|
||||
],
|
||||
303 => [
|
||||
'switch' => [
|
||||
'unless' => ['GET', 'HEAD'],
|
||||
'to' => 'GET',
|
||||
],
|
||||
'multiple' => false,
|
||||
'permanent' => false,
|
||||
],
|
||||
307 => [
|
||||
'switch' => false,
|
||||
'multiple' => false,
|
||||
'permanent' => false,
|
||||
],
|
||||
308 => [
|
||||
'switch' => false,
|
||||
'multiple' => false,
|
||||
'permanent' => true,
|
||||
],
|
||||
];
|
||||
|
||||
/**
|
||||
* Determine how header should be preserved from old request.
|
||||
*
|
||||
* @var bool|array
|
||||
*
|
||||
* true will keep all previous headers (default value)
|
||||
* false will ditch all previous headers
|
||||
* string[] will keep only headers with the specified names
|
||||
*/
|
||||
private $preserveHeader;
|
||||
|
||||
/**
|
||||
* Store all previous redirect from 301 / 308 status code.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
private $redirectStorage = [];
|
||||
|
||||
/**
|
||||
* Whether the location header must be directly used for a multiple redirection status code (300).
|
||||
*
|
||||
* @var bool
|
||||
*/
|
||||
private $useDefaultForMultiple;
|
||||
|
||||
/**
|
||||
* @var string[][] Chain identifier => list of URLs for this chain
|
||||
*/
|
||||
private $circularDetection = [];
|
||||
|
||||
/**
|
||||
* @param array $config {
|
||||
*
|
||||
* @var bool|string[] $preserve_header True keeps all headers, false remove all of them, an array is interpreted as a list of header names to keep
|
||||
* @var bool $use_default_for_multiple Whether the location header must be directly used for a multiple redirection status code (300).
|
||||
* }
|
||||
*/
|
||||
public function __construct(array $config = [])
|
||||
{
|
||||
$resolver = new OptionsResolver();
|
||||
$resolver->setDefaults([
|
||||
'preserve_header' => true,
|
||||
'use_default_for_multiple' => true,
|
||||
]);
|
||||
$resolver->setAllowedTypes('preserve_header', ['bool', 'array']);
|
||||
$resolver->setAllowedTypes('use_default_for_multiple', 'bool');
|
||||
$resolver->setNormalizer('preserve_header', function (OptionsResolver $resolver, $value) {
|
||||
if (is_bool($value) && false === $value) {
|
||||
return [];
|
||||
}
|
||||
|
||||
return $value;
|
||||
});
|
||||
$options = $resolver->resolve($config);
|
||||
|
||||
$this->preserveHeader = $options['preserve_header'];
|
||||
$this->useDefaultForMultiple = $options['use_default_for_multiple'];
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function handleRequest(RequestInterface $request, callable $next, callable $first): Promise
|
||||
{
|
||||
// Check in storage
|
||||
if (array_key_exists((string) $request->getUri(), $this->redirectStorage)) {
|
||||
$uri = $this->redirectStorage[(string) $request->getUri()]['uri'];
|
||||
$statusCode = $this->redirectStorage[(string) $request->getUri()]['status'];
|
||||
$redirectRequest = $this->buildRedirectRequest($request, $uri, $statusCode);
|
||||
|
||||
return $first($redirectRequest);
|
||||
}
|
||||
|
||||
return $next($request)->then(function (ResponseInterface $response) use ($request, $first): ResponseInterface {
|
||||
$statusCode = $response->getStatusCode();
|
||||
|
||||
if (!array_key_exists($statusCode, $this->redirectCodes)) {
|
||||
return $response;
|
||||
}
|
||||
|
||||
$uri = $this->createUri($response, $request);
|
||||
$redirectRequest = $this->buildRedirectRequest($request, $uri, $statusCode);
|
||||
$chainIdentifier = spl_object_hash((object) $first);
|
||||
|
||||
if (!array_key_exists($chainIdentifier, $this->circularDetection)) {
|
||||
$this->circularDetection[$chainIdentifier] = [];
|
||||
}
|
||||
|
||||
$this->circularDetection[$chainIdentifier][] = (string) $request->getUri();
|
||||
|
||||
if (in_array((string) $redirectRequest->getUri(), $this->circularDetection[$chainIdentifier])) {
|
||||
throw new CircularRedirectionException('Circular redirection detected', $request, $response);
|
||||
}
|
||||
|
||||
if ($this->redirectCodes[$statusCode]['permanent']) {
|
||||
$this->redirectStorage[(string) $request->getUri()] = [
|
||||
'uri' => $uri,
|
||||
'status' => $statusCode,
|
||||
];
|
||||
}
|
||||
|
||||
// Call redirect request synchronously
|
||||
$redirectPromise = $first($redirectRequest);
|
||||
|
||||
return $redirectPromise->wait();
|
||||
});
|
||||
}
|
||||
|
||||
private function buildRedirectRequest(RequestInterface $originalRequest, UriInterface $targetUri, int $statusCode): RequestInterface
|
||||
{
|
||||
$originalRequest = $originalRequest->withUri($targetUri);
|
||||
|
||||
if (false !== $this->redirectCodes[$statusCode]['switch'] && !in_array($originalRequest->getMethod(), $this->redirectCodes[$statusCode]['switch']['unless'])) {
|
||||
$originalRequest = $originalRequest->withMethod($this->redirectCodes[$statusCode]['switch']['to']);
|
||||
}
|
||||
|
||||
if (is_array($this->preserveHeader)) {
|
||||
$headers = array_keys($originalRequest->getHeaders());
|
||||
|
||||
foreach ($headers as $name) {
|
||||
if (!in_array($name, $this->preserveHeader)) {
|
||||
$originalRequest = $originalRequest->withoutHeader($name);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $originalRequest;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new Uri from the old request and the location header.
|
||||
*
|
||||
* @throws HttpException If location header is not usable (missing or incorrect)
|
||||
* @throws MultipleRedirectionException If a 300 status code is received and default location cannot be resolved (doesn't use the location header or not present)
|
||||
*/
|
||||
private function createUri(ResponseInterface $redirectResponse, RequestInterface $originalRequest): UriInterface
|
||||
{
|
||||
if ($this->redirectCodes[$redirectResponse->getStatusCode()]['multiple'] && (!$this->useDefaultForMultiple || !$redirectResponse->hasHeader('Location'))) {
|
||||
throw new MultipleRedirectionException('Cannot choose a redirection', $originalRequest, $redirectResponse);
|
||||
}
|
||||
|
||||
if (!$redirectResponse->hasHeader('Location')) {
|
||||
throw new HttpException('Redirect status code, but no location header present in the response', $originalRequest, $redirectResponse);
|
||||
}
|
||||
|
||||
$location = $redirectResponse->getHeaderLine('Location');
|
||||
$parsedLocation = parse_url($location);
|
||||
|
||||
if (false === $parsedLocation) {
|
||||
throw new HttpException(sprintf('Location %s could not be parsed', $location), $originalRequest, $redirectResponse);
|
||||
}
|
||||
|
||||
$uri = $originalRequest->getUri();
|
||||
|
||||
if (array_key_exists('scheme', $parsedLocation)) {
|
||||
$uri = $uri->withScheme($parsedLocation['scheme']);
|
||||
}
|
||||
|
||||
if (array_key_exists('host', $parsedLocation)) {
|
||||
$uri = $uri->withHost($parsedLocation['host']);
|
||||
}
|
||||
|
||||
if (array_key_exists('port', $parsedLocation)) {
|
||||
$uri = $uri->withPort($parsedLocation['port']);
|
||||
}
|
||||
|
||||
if (array_key_exists('path', $parsedLocation)) {
|
||||
$uri = $uri->withPath($parsedLocation['path']);
|
||||
}
|
||||
|
||||
if (array_key_exists('query', $parsedLocation)) {
|
||||
$uri = $uri->withQuery($parsedLocation['query']);
|
||||
} else {
|
||||
$uri = $uri->withQuery('');
|
||||
}
|
||||
|
||||
if (array_key_exists('fragment', $parsedLocation)) {
|
||||
$uri = $uri->withFragment($parsedLocation['fragment']);
|
||||
} else {
|
||||
$uri = $uri->withFragment('');
|
||||
}
|
||||
|
||||
return $uri;
|
||||
}
|
||||
}
|
||||
56
vendor/php-http/client-common/src/Plugin/RequestMatcherPlugin.php
vendored
Normal file
56
vendor/php-http/client-common/src/Plugin/RequestMatcherPlugin.php
vendored
Normal file
@@ -0,0 +1,56 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Http\Client\Common\Plugin;
|
||||
|
||||
use Http\Client\Common\Plugin;
|
||||
use Http\Message\RequestMatcher;
|
||||
use Http\Promise\Promise;
|
||||
use Psr\Http\Message\RequestInterface;
|
||||
|
||||
/**
|
||||
* Apply a delegated plugin based on a request match.
|
||||
*
|
||||
* @author Márk Sági-Kazár <mark.sagikazar@gmail.com>
|
||||
*/
|
||||
final class RequestMatcherPlugin implements Plugin
|
||||
{
|
||||
/**
|
||||
* @var RequestMatcher
|
||||
*/
|
||||
private $requestMatcher;
|
||||
|
||||
/**
|
||||
* @var Plugin|null
|
||||
*/
|
||||
private $successPlugin;
|
||||
|
||||
/**
|
||||
* @var Plugin|null
|
||||
*/
|
||||
private $failurePlugin;
|
||||
|
||||
public function __construct(RequestMatcher $requestMatcher, ?Plugin $delegateOnMatch, Plugin $delegateOnNoMatch = null)
|
||||
{
|
||||
$this->requestMatcher = $requestMatcher;
|
||||
$this->successPlugin = $delegateOnMatch;
|
||||
$this->failurePlugin = $delegateOnNoMatch;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function handleRequest(RequestInterface $request, callable $next, callable $first): Promise
|
||||
{
|
||||
if ($this->requestMatcher->matches($request)) {
|
||||
if (null !== $this->successPlugin) {
|
||||
return $this->successPlugin->handleRequest($request, $next, $first);
|
||||
}
|
||||
} elseif (null !== $this->failurePlugin) {
|
||||
return $this->failurePlugin->handleRequest($request, $next, $first);
|
||||
}
|
||||
|
||||
return $next($request);
|
||||
}
|
||||
}
|
||||
29
vendor/php-http/client-common/src/Plugin/RequestSeekableBodyPlugin.php
vendored
Normal file
29
vendor/php-http/client-common/src/Plugin/RequestSeekableBodyPlugin.php
vendored
Normal file
@@ -0,0 +1,29 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Http\Client\Common\Plugin;
|
||||
|
||||
use Http\Message\Stream\BufferedStream;
|
||||
use Http\Promise\Promise;
|
||||
use Psr\Http\Message\RequestInterface;
|
||||
|
||||
/**
|
||||
* Allow body used in request to be always seekable.
|
||||
*
|
||||
* @author Joel Wurtz <joel.wurtz@gmail.com>
|
||||
*/
|
||||
final class RequestSeekableBodyPlugin extends SeekableBodyPlugin
|
||||
{
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function handleRequest(RequestInterface $request, callable $next, callable $first): Promise
|
||||
{
|
||||
if (!$request->getBody()->isSeekable()) {
|
||||
$request = $request->withBody(new BufferedStream($request->getBody(), $this->useFileBuffer, $this->memoryBufferSize));
|
||||
}
|
||||
|
||||
return $next($request);
|
||||
}
|
||||
}
|
||||
32
vendor/php-http/client-common/src/Plugin/ResponseSeekableBodyPlugin.php
vendored
Normal file
32
vendor/php-http/client-common/src/Plugin/ResponseSeekableBodyPlugin.php
vendored
Normal file
@@ -0,0 +1,32 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Http\Client\Common\Plugin;
|
||||
|
||||
use Http\Message\Stream\BufferedStream;
|
||||
use Http\Promise\Promise;
|
||||
use Psr\Http\Message\RequestInterface;
|
||||
use Psr\Http\Message\ResponseInterface;
|
||||
|
||||
/**
|
||||
* Allow body used in response to be always seekable.
|
||||
*
|
||||
* @author Joel Wurtz <joel.wurtz@gmail.com>
|
||||
*/
|
||||
final class ResponseSeekableBodyPlugin extends SeekableBodyPlugin
|
||||
{
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function handleRequest(RequestInterface $request, callable $next, callable $first): Promise
|
||||
{
|
||||
return $next($request)->then(function (ResponseInterface $response) {
|
||||
if ($response->getBody()->isSeekable()) {
|
||||
return $response;
|
||||
}
|
||||
|
||||
return $response->withBody(new BufferedStream($response->getBody(), $this->useFileBuffer, $this->memoryBufferSize));
|
||||
});
|
||||
}
|
||||
}
|
||||
177
vendor/php-http/client-common/src/Plugin/RetryPlugin.php
vendored
Normal file
177
vendor/php-http/client-common/src/Plugin/RetryPlugin.php
vendored
Normal file
@@ -0,0 +1,177 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Http\Client\Common\Plugin;
|
||||
|
||||
use Http\Client\Common\Plugin;
|
||||
use Http\Client\Exception\HttpException;
|
||||
use Http\Promise\Promise;
|
||||
use Psr\Http\Client\ClientExceptionInterface;
|
||||
use Psr\Http\Message\RequestInterface;
|
||||
use Psr\Http\Message\ResponseInterface;
|
||||
use Symfony\Component\OptionsResolver\OptionsResolver;
|
||||
|
||||
/**
|
||||
* Retry the request if an exception is thrown.
|
||||
*
|
||||
* By default will retry only one time.
|
||||
*
|
||||
* @author Joel Wurtz <joel.wurtz@gmail.com>
|
||||
*/
|
||||
final class RetryPlugin implements Plugin
|
||||
{
|
||||
/**
|
||||
* Number of retry before sending an exception.
|
||||
*
|
||||
* @var int
|
||||
*/
|
||||
private $retry;
|
||||
|
||||
/**
|
||||
* @var callable
|
||||
*/
|
||||
private $errorResponseDelay;
|
||||
|
||||
/**
|
||||
* @var callable
|
||||
*/
|
||||
private $errorResponseDecider;
|
||||
|
||||
/**
|
||||
* @var callable
|
||||
*/
|
||||
private $exceptionDecider;
|
||||
|
||||
/**
|
||||
* @var callable
|
||||
*/
|
||||
private $exceptionDelay;
|
||||
|
||||
/**
|
||||
* Store the retry counter for each request.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
private $retryStorage = [];
|
||||
|
||||
/**
|
||||
* @param array $config {
|
||||
*
|
||||
* @var int $retries Number of retries to attempt if an exception occurs before letting the exception bubble up
|
||||
* @var callable $error_response_decider A callback that gets a request and response to decide whether the request should be retried
|
||||
* @var callable $exception_decider A callback that gets a request and an exception to decide after a failure whether the request should be retried
|
||||
* @var callable $error_response_delay A callback that gets a request and response and the current number of retries and returns how many microseconds we should wait before trying again
|
||||
* @var callable $exception_delay A callback that gets a request, an exception and the current number of retries and returns how many microseconds we should wait before trying again
|
||||
* }
|
||||
*/
|
||||
public function __construct(array $config = [])
|
||||
{
|
||||
$resolver = new OptionsResolver();
|
||||
$resolver->setDefaults([
|
||||
'retries' => 1,
|
||||
'error_response_decider' => function (RequestInterface $request, ResponseInterface $response) {
|
||||
// do not retry client errors
|
||||
return $response->getStatusCode() >= 500 && $response->getStatusCode() < 600;
|
||||
},
|
||||
'exception_decider' => function (RequestInterface $request, ClientExceptionInterface $e) {
|
||||
// do not retry client errors
|
||||
return !$e instanceof HttpException || $e->getCode() >= 500 && $e->getCode() < 600;
|
||||
},
|
||||
'error_response_delay' => __CLASS__.'::defaultErrorResponseDelay',
|
||||
'exception_delay' => __CLASS__.'::defaultExceptionDelay',
|
||||
]);
|
||||
|
||||
$resolver->setAllowedTypes('retries', 'int');
|
||||
$resolver->setAllowedTypes('error_response_decider', 'callable');
|
||||
$resolver->setAllowedTypes('exception_decider', 'callable');
|
||||
$resolver->setAllowedTypes('error_response_delay', 'callable');
|
||||
$resolver->setAllowedTypes('exception_delay', 'callable');
|
||||
$options = $resolver->resolve($config);
|
||||
|
||||
$this->retry = $options['retries'];
|
||||
$this->errorResponseDecider = $options['error_response_decider'];
|
||||
$this->errorResponseDelay = $options['error_response_delay'];
|
||||
$this->exceptionDecider = $options['exception_decider'];
|
||||
$this->exceptionDelay = $options['exception_delay'];
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function handleRequest(RequestInterface $request, callable $next, callable $first): Promise
|
||||
{
|
||||
$chainIdentifier = spl_object_hash((object) $first);
|
||||
|
||||
return $next($request)->then(function (ResponseInterface $response) use ($request, $next, $first, $chainIdentifier) {
|
||||
if (!array_key_exists($chainIdentifier, $this->retryStorage)) {
|
||||
$this->retryStorage[$chainIdentifier] = 0;
|
||||
}
|
||||
|
||||
if ($this->retryStorage[$chainIdentifier] >= $this->retry) {
|
||||
unset($this->retryStorage[$chainIdentifier]);
|
||||
|
||||
return $response;
|
||||
}
|
||||
|
||||
if (call_user_func($this->errorResponseDecider, $request, $response)) {
|
||||
$time = call_user_func($this->errorResponseDelay, $request, $response, $this->retryStorage[$chainIdentifier]);
|
||||
$response = $this->retry($request, $next, $first, $chainIdentifier, $time);
|
||||
}
|
||||
|
||||
if (array_key_exists($chainIdentifier, $this->retryStorage)) {
|
||||
unset($this->retryStorage[$chainIdentifier]);
|
||||
}
|
||||
|
||||
return $response;
|
||||
}, function (ClientExceptionInterface $exception) use ($request, $next, $first, $chainIdentifier) {
|
||||
if (!array_key_exists($chainIdentifier, $this->retryStorage)) {
|
||||
$this->retryStorage[$chainIdentifier] = 0;
|
||||
}
|
||||
|
||||
if ($this->retryStorage[$chainIdentifier] >= $this->retry) {
|
||||
unset($this->retryStorage[$chainIdentifier]);
|
||||
|
||||
throw $exception;
|
||||
}
|
||||
|
||||
if (!call_user_func($this->exceptionDecider, $request, $exception)) {
|
||||
throw $exception;
|
||||
}
|
||||
|
||||
$time = call_user_func($this->exceptionDelay, $request, $exception, $this->retryStorage[$chainIdentifier]);
|
||||
|
||||
return $this->retry($request, $next, $first, $chainIdentifier, $time);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* @param int $retries The number of retries we made before. First time this get called it will be 0.
|
||||
*/
|
||||
public static function defaultErrorResponseDelay(RequestInterface $request, ResponseInterface $response, int $retries): int
|
||||
{
|
||||
return pow(2, $retries) * 500000;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param int $retries The number of retries we made before. First time this get called it will be 0.
|
||||
*/
|
||||
public static function defaultExceptionDelay(RequestInterface $request, ClientExceptionInterface $e, int $retries): int
|
||||
{
|
||||
return pow(2, $retries) * 500000;
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws \Exception if retrying returns a failed promise
|
||||
*/
|
||||
private function retry(RequestInterface $request, callable $next, callable $first, string $chainIdentifier, int $delay): ResponseInterface
|
||||
{
|
||||
usleep($delay);
|
||||
|
||||
// Retry synchronously
|
||||
++$this->retryStorage[$chainIdentifier];
|
||||
$promise = $this->handleRequest($request, $next, $first);
|
||||
|
||||
return $promise->wait();
|
||||
}
|
||||
}
|
||||
47
vendor/php-http/client-common/src/Plugin/SeekableBodyPlugin.php
vendored
Normal file
47
vendor/php-http/client-common/src/Plugin/SeekableBodyPlugin.php
vendored
Normal file
@@ -0,0 +1,47 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Http\Client\Common\Plugin;
|
||||
|
||||
use Http\Client\Common\Plugin;
|
||||
use Symfony\Component\OptionsResolver\OptionsResolver;
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
abstract class SeekableBodyPlugin implements Plugin
|
||||
{
|
||||
/**
|
||||
* @var bool
|
||||
*/
|
||||
protected $useFileBuffer;
|
||||
|
||||
/**
|
||||
* @var int
|
||||
*/
|
||||
protected $memoryBufferSize;
|
||||
|
||||
/**
|
||||
* @param array $config {
|
||||
*
|
||||
* @var bool $use_file_buffer Whether this plugin should use a file as a buffer if the stream is too big, defaults to true
|
||||
* @var int $memory_buffer_size Max memory size in bytes to use for the buffer before it use a file, defaults to 2097152 (2 mb)
|
||||
* }
|
||||
*/
|
||||
public function __construct(array $config = [])
|
||||
{
|
||||
$resolver = new OptionsResolver();
|
||||
$resolver->setDefaults([
|
||||
'use_file_buffer' => true,
|
||||
'memory_buffer_size' => 2097152,
|
||||
]);
|
||||
$resolver->setAllowedTypes('use_file_buffer', 'bool');
|
||||
$resolver->setAllowedTypes('memory_buffer_size', 'int');
|
||||
|
||||
$options = $resolver->resolve($config);
|
||||
|
||||
$this->useFileBuffer = $options['use_file_buffer'];
|
||||
$this->memoryBufferSize = $options['memory_buffer_size'];
|
||||
}
|
||||
}
|
||||
24
vendor/php-http/client-common/src/Plugin/VersionBridgePlugin.php
vendored
Normal file
24
vendor/php-http/client-common/src/Plugin/VersionBridgePlugin.php
vendored
Normal file
@@ -0,0 +1,24 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Http\Client\Common\Plugin;
|
||||
|
||||
use Http\Promise\Promise;
|
||||
use Psr\Http\Message\RequestInterface;
|
||||
|
||||
/**
|
||||
* A plugin that helps you migrate from php-http/client-common 1.x to 2.x. This
|
||||
* will also help you to support PHP5 at the same time you support 2.x.
|
||||
*
|
||||
* @author Tobias Nyholm <tobias.nyholm@gmail.com>
|
||||
*/
|
||||
trait VersionBridgePlugin
|
||||
{
|
||||
abstract protected function doHandleRequest(RequestInterface $request, callable $next, callable $first);
|
||||
|
||||
public function handleRequest(RequestInterface $request, callable $next, callable $first): Promise
|
||||
{
|
||||
return $this->doHandleRequest($request, $next, $first);
|
||||
}
|
||||
}
|
||||
65
vendor/php-http/client-common/src/PluginChain.php
vendored
Normal file
65
vendor/php-http/client-common/src/PluginChain.php
vendored
Normal file
@@ -0,0 +1,65 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Http\Client\Common;
|
||||
|
||||
use function array_reverse;
|
||||
use Http\Client\Common\Exception\LoopException;
|
||||
use Http\Promise\Promise;
|
||||
use Psr\Http\Message\RequestInterface;
|
||||
|
||||
final class PluginChain
|
||||
{
|
||||
/** @var Plugin[] */
|
||||
private $plugins;
|
||||
|
||||
/** @var callable(RequestInterface): Promise */
|
||||
private $clientCallable;
|
||||
|
||||
/** @var int */
|
||||
private $maxRestarts;
|
||||
|
||||
/** @var int */
|
||||
private $restarts = 0;
|
||||
|
||||
/**
|
||||
* @param Plugin[] $plugins A plugin chain
|
||||
* @param callable(RequestInterface): Promise $clientCallable Callable making the HTTP call
|
||||
* @param array $options {
|
||||
*
|
||||
* @var int $max_restarts
|
||||
* }
|
||||
*/
|
||||
public function __construct(array $plugins, callable $clientCallable, array $options = [])
|
||||
{
|
||||
$this->plugins = $plugins;
|
||||
$this->clientCallable = $clientCallable;
|
||||
$this->maxRestarts = (int) ($options['max_restarts'] ?? 0);
|
||||
}
|
||||
|
||||
private function createChain(): callable
|
||||
{
|
||||
$lastCallable = $this->clientCallable;
|
||||
$reversedPlugins = array_reverse($this->plugins);
|
||||
|
||||
foreach ($reversedPlugins as $plugin) {
|
||||
$lastCallable = function (RequestInterface $request) use ($plugin, $lastCallable) {
|
||||
return $plugin->handleRequest($request, $lastCallable, $this);
|
||||
};
|
||||
}
|
||||
|
||||
return $lastCallable;
|
||||
}
|
||||
|
||||
public function __invoke(RequestInterface $request): Promise
|
||||
{
|
||||
if ($this->restarts > $this->maxRestarts) {
|
||||
throw new LoopException('Too many restarts in plugin client', $request);
|
||||
}
|
||||
|
||||
++$this->restarts;
|
||||
|
||||
return $this->createChain()($request);
|
||||
}
|
||||
}
|
||||
133
vendor/php-http/client-common/src/PluginClient.php
vendored
Normal file
133
vendor/php-http/client-common/src/PluginClient.php
vendored
Normal file
@@ -0,0 +1,133 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Http\Client\Common;
|
||||
|
||||
use Http\Client\Exception as HttplugException;
|
||||
use Http\Client\HttpAsyncClient;
|
||||
use Http\Client\HttpClient;
|
||||
use Http\Client\Promise\HttpFulfilledPromise;
|
||||
use Http\Client\Promise\HttpRejectedPromise;
|
||||
use Http\Promise\Promise;
|
||||
use Psr\Http\Client\ClientInterface;
|
||||
use Psr\Http\Message\RequestInterface;
|
||||
use Psr\Http\Message\ResponseInterface;
|
||||
use Symfony\Component\OptionsResolver\OptionsResolver;
|
||||
|
||||
/**
|
||||
* The client managing plugins and providing a decorator around HTTP Clients.
|
||||
*
|
||||
* @author Joel Wurtz <joel.wurtz@gmail.com>
|
||||
*/
|
||||
final class PluginClient implements HttpClient, HttpAsyncClient
|
||||
{
|
||||
/**
|
||||
* An HTTP async client.
|
||||
*
|
||||
* @var HttpAsyncClient
|
||||
*/
|
||||
private $client;
|
||||
|
||||
/**
|
||||
* The plugin chain.
|
||||
*
|
||||
* @var Plugin[]
|
||||
*/
|
||||
private $plugins;
|
||||
|
||||
/**
|
||||
* A list of options.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
private $options;
|
||||
|
||||
/**
|
||||
* @param ClientInterface|HttpAsyncClient $client An HTTP async client
|
||||
* @param Plugin[] $plugins A plugin chain
|
||||
* @param array $options {
|
||||
*
|
||||
* @var int $max_restarts
|
||||
* }
|
||||
*/
|
||||
public function __construct($client, array $plugins = [], array $options = [])
|
||||
{
|
||||
if ($client instanceof HttpAsyncClient) {
|
||||
$this->client = $client;
|
||||
} elseif ($client instanceof ClientInterface) {
|
||||
$this->client = new EmulatedHttpAsyncClient($client);
|
||||
} else {
|
||||
throw new \TypeError(
|
||||
sprintf('%s::__construct(): Argument #1 ($client) must be of type %s|%s, %s given', self::class, ClientInterface::class, HttpAsyncClient::class, get_debug_type($client))
|
||||
);
|
||||
}
|
||||
|
||||
$this->plugins = $plugins;
|
||||
$this->options = $this->configure($options);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function sendRequest(RequestInterface $request): ResponseInterface
|
||||
{
|
||||
// If the client doesn't support sync calls, call async
|
||||
if (!$this->client instanceof ClientInterface) {
|
||||
return $this->sendAsyncRequest($request)->wait();
|
||||
}
|
||||
|
||||
// Else we want to use the synchronous call of the underlying client,
|
||||
// and not the async one in the case we have both an async and sync call
|
||||
$pluginChain = $this->createPluginChain($this->plugins, function (RequestInterface $request) {
|
||||
try {
|
||||
return new HttpFulfilledPromise($this->client->sendRequest($request));
|
||||
} catch (HttplugException $exception) {
|
||||
return new HttpRejectedPromise($exception);
|
||||
}
|
||||
});
|
||||
|
||||
return $pluginChain($request)->wait();
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function sendAsyncRequest(RequestInterface $request)
|
||||
{
|
||||
$pluginChain = $this->createPluginChain($this->plugins, function (RequestInterface $request) {
|
||||
return $this->client->sendAsyncRequest($request);
|
||||
});
|
||||
|
||||
return $pluginChain($request);
|
||||
}
|
||||
|
||||
/**
|
||||
* Configure the plugin client.
|
||||
*/
|
||||
private function configure(array $options = []): array
|
||||
{
|
||||
$resolver = new OptionsResolver();
|
||||
$resolver->setDefaults([
|
||||
'max_restarts' => 10,
|
||||
]);
|
||||
|
||||
$resolver->setAllowedTypes('max_restarts', 'int');
|
||||
|
||||
return $resolver->resolve($options);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create the plugin chain.
|
||||
*
|
||||
* @param Plugin[] $plugins A plugin chain
|
||||
* @param callable $clientCallable Callable making the HTTP call
|
||||
*
|
||||
* @return callable(RequestInterface): Promise
|
||||
*/
|
||||
private function createPluginChain(array $plugins, callable $clientCallable): callable
|
||||
{
|
||||
/** @var callable(RequestInterface): Promise */
|
||||
return new PluginChain($plugins, $clientCallable, $this->options);
|
||||
}
|
||||
}
|
||||
76
vendor/php-http/client-common/src/PluginClientBuilder.php
vendored
Normal file
76
vendor/php-http/client-common/src/PluginClientBuilder.php
vendored
Normal file
@@ -0,0 +1,76 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Http\Client\Common;
|
||||
|
||||
use Http\Client\HttpAsyncClient;
|
||||
use Psr\Http\Client\ClientInterface;
|
||||
|
||||
/**
|
||||
* Build an instance of a PluginClient with a dynamic list of plugins.
|
||||
*
|
||||
* @author Baptiste Clavié <clavie.b@gmail.com>
|
||||
*/
|
||||
final class PluginClientBuilder
|
||||
{
|
||||
/** @var Plugin[][] List of plugins ordered by priority [priority => Plugin[]]). */
|
||||
private $plugins = [];
|
||||
|
||||
/** @var array Array of options to give to the plugin client */
|
||||
private $options = [];
|
||||
|
||||
/**
|
||||
* @param int $priority Priority of the plugin. The higher comes first.
|
||||
*/
|
||||
public function addPlugin(Plugin $plugin, int $priority = 0): self
|
||||
{
|
||||
$this->plugins[$priority][] = $plugin;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param mixed $value
|
||||
*/
|
||||
public function setOption(string $name, $value): self
|
||||
{
|
||||
$this->options[$name] = $value;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function removeOption(string $name): self
|
||||
{
|
||||
unset($this->options[$name]);
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param ClientInterface|HttpAsyncClient $client
|
||||
*/
|
||||
public function createClient($client): PluginClient
|
||||
{
|
||||
if (!$client instanceof ClientInterface && !$client instanceof HttpAsyncClient) {
|
||||
throw new \TypeError(
|
||||
sprintf('%s::createClient(): Argument #1 ($client) must be of type %s|%s, %s given', self::class, ClientInterface::class, HttpAsyncClient::class, get_debug_type($client))
|
||||
);
|
||||
}
|
||||
|
||||
$plugins = $this->plugins;
|
||||
|
||||
if (0 === count($plugins)) {
|
||||
$plugins[] = [];
|
||||
}
|
||||
|
||||
krsort($plugins);
|
||||
$plugins = array_merge(...$plugins);
|
||||
|
||||
return new PluginClient(
|
||||
$client,
|
||||
array_values($plugins),
|
||||
$this->options
|
||||
);
|
||||
}
|
||||
}
|
||||
68
vendor/php-http/client-common/src/PluginClientFactory.php
vendored
Normal file
68
vendor/php-http/client-common/src/PluginClientFactory.php
vendored
Normal file
@@ -0,0 +1,68 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Http\Client\Common;
|
||||
|
||||
use Http\Client\HttpAsyncClient;
|
||||
use Psr\Http\Client\ClientInterface;
|
||||
|
||||
/**
|
||||
* Factory to create PluginClient instances. Using this factory instead of calling PluginClient constructor will enable
|
||||
* the Symfony profiling without any configuration.
|
||||
*
|
||||
* @author Fabien Bourigault <bourigaultfabien@gmail.com>
|
||||
*/
|
||||
final class PluginClientFactory
|
||||
{
|
||||
/**
|
||||
* @var (callable(ClientInterface|HttpAsyncClient, Plugin[], array): PluginClient)|null
|
||||
*/
|
||||
private static $factory;
|
||||
|
||||
/**
|
||||
* Set the factory to use.
|
||||
* The callable to provide must have the same arguments and return type as PluginClientFactory::createClient.
|
||||
* This is used by the HTTPlugBundle to provide a better Symfony integration.
|
||||
* Unlike the createClient method, this one is static to allow zero configuration profiling by hooking into early
|
||||
* application execution.
|
||||
*
|
||||
* @internal
|
||||
*
|
||||
* @param callable(ClientInterface|HttpAsyncClient, Plugin[], array): PluginClient $factory
|
||||
*/
|
||||
public static function setFactory(callable $factory): void
|
||||
{
|
||||
static::$factory = $factory;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param ClientInterface|HttpAsyncClient $client
|
||||
* @param Plugin[] $plugins
|
||||
* @param array $options {
|
||||
*
|
||||
* @var string $client_name to give client a name which may be used when displaying client information like in
|
||||
* the HTTPlugBundle profiler.
|
||||
* }
|
||||
*
|
||||
* @see PluginClient constructor for PluginClient specific $options.
|
||||
*/
|
||||
public function createClient($client, array $plugins = [], array $options = []): PluginClient
|
||||
{
|
||||
if (!$client instanceof ClientInterface && !$client instanceof HttpAsyncClient) {
|
||||
throw new \TypeError(
|
||||
sprintf('%s::createClient(): Argument #1 ($client) must be of type %s|%s, %s given', self::class, ClientInterface::class, HttpAsyncClient::class, get_debug_type($client))
|
||||
);
|
||||
}
|
||||
|
||||
if (static::$factory) {
|
||||
$factory = static::$factory;
|
||||
|
||||
return $factory($client, $plugins, $options);
|
||||
}
|
||||
|
||||
unset($options['client_name']);
|
||||
|
||||
return new PluginClient($client, $plugins, $options);
|
||||
}
|
||||
}
|
||||
24
vendor/php-http/client-common/src/VersionBridgeClient.php
vendored
Normal file
24
vendor/php-http/client-common/src/VersionBridgeClient.php
vendored
Normal file
@@ -0,0 +1,24 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Http\Client\Common;
|
||||
|
||||
use Psr\Http\Message\RequestInterface;
|
||||
use Psr\Http\Message\ResponseInterface;
|
||||
|
||||
/**
|
||||
* A client that helps you migrate from php-http/httplug 1.x to 2.x. This
|
||||
* will also help you to support PHP5 at the same time you support 2.x.
|
||||
*
|
||||
* @author Tobias Nyholm <tobias.nyholm@gmail.com>
|
||||
*/
|
||||
trait VersionBridgeClient
|
||||
{
|
||||
abstract protected function doSendRequest(RequestInterface $request);
|
||||
|
||||
public function sendRequest(RequestInterface $request): ResponseInterface
|
||||
{
|
||||
return $this->doSendRequest($request);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user