feature #30604 [HttpClient] add MockHttpClient (nicolas-grekas)
This PR was merged into the 4.3-dev branch.
Discussion
----------
[HttpClient] add MockHttpClient
| Q | A
| ------------- | ---
| Branch? | master
| Bug fix? | no
| New feature? | yes
| BC breaks? | no
| Deprecations? | no
| Tests pass? | yes
| Fixed tickets | -
| License | MIT
| Doc PR | -
This PR introduces `MockHttpClient` and `MockResponse`, to be used for testing classes that need an HTTP client without making actual HTTP requests.
`MockHttpClient` is configured via its constructor: you provide it either with an iterable or a callable, and these will be used to provide responses as the consumer requests them.
Example:
```php
$responses = [
new MockResponse($body1, $info1),
new MockResponse($body2, $info2),
];
$client = new MockHttpClient($responses);
$response1 = $client->request(...); // created from $responses[0]
$response2 = $client->request(...); // created from $responses[1]
```
Or alternatively:
```php
$callback = function ($method, $url, $options) {
return new MockResponse(...);
};
$client = new MockHttpClient($callback);
$response = $client->request(...); // calls $callback internal
```
The responses provided to the client don't have to be instances of `MockResponse` - any `ResponseInterface` works (e.g. `$this->getMockBuilder(ResponseInterface::class)->getMock()`).
Using `MockResponse` allows simulating chunked responses and timeouts:
```php
$body = function () {
yield 'hello';
yield ''; // the empty string is turned into a timeout so that they are easy to test
yield 'world';
};
$mockResponse = new Mockresponse($body);
```
Last but not least, the implementation simulates the full lifecycle of a properly behaving `HttpClientInterface` contracts implementation: error handling, progress function, etc. This is "proved" by `MockHttpClientTest`, who implements and passes the reference test suite in `HttpClientTestCase`.
Commits
-------
8fd7584158
[HttpClient] add MockHttpClient
This commit is contained in:
commit
d5d1b50cf7
@ -21,19 +21,14 @@ use Symfony\Contracts\HttpClient\ChunkInterface;
|
|||||||
*/
|
*/
|
||||||
class ErrorChunk implements ChunkInterface
|
class ErrorChunk implements ChunkInterface
|
||||||
{
|
{
|
||||||
protected $didThrow;
|
private $didThrow = false;
|
||||||
|
|
||||||
private $offset;
|
private $offset;
|
||||||
private $errorMessage;
|
private $errorMessage;
|
||||||
private $error;
|
private $error;
|
||||||
|
|
||||||
/**
|
public function __construct(int $offset, \Throwable $error = null)
|
||||||
* @param bool &$didThrow Allows monitoring when the $error has been thrown or not
|
|
||||||
*/
|
|
||||||
public function __construct(bool &$didThrow, int $offset, \Throwable $error = null)
|
|
||||||
{
|
{
|
||||||
$didThrow = false;
|
$didThrow = false;
|
||||||
$this->didThrow = &$didThrow;
|
|
||||||
$this->offset = $offset;
|
$this->offset = $offset;
|
||||||
$this->error = $error;
|
$this->error = $error;
|
||||||
$this->errorMessage = null !== $error ? $error->getMessage() : 'Reading from the response stream reached the inactivity timeout.';
|
$this->errorMessage = null !== $error ? $error->getMessage() : 'Reading from the response stream reached the inactivity timeout.';
|
||||||
@ -96,6 +91,14 @@ class ErrorChunk implements ChunkInterface
|
|||||||
return $this->errorMessage;
|
return $this->errorMessage;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return bool Whether the wrapped error has been thrown or not
|
||||||
|
*/
|
||||||
|
public function didThrow(): bool
|
||||||
|
{
|
||||||
|
return $this->didThrow;
|
||||||
|
}
|
||||||
|
|
||||||
public function __destruct()
|
public function __destruct()
|
||||||
{
|
{
|
||||||
if (!$this->didThrow) {
|
if (!$this->didThrow) {
|
||||||
|
@ -117,7 +117,7 @@ trait HttpClientTrait
|
|||||||
|
|
||||||
// Finalize normalization of options
|
// Finalize normalization of options
|
||||||
$options['headers'] = $headers;
|
$options['headers'] = $headers;
|
||||||
$options['http_version'] = (string) ($options['http_version'] ?? '');
|
$options['http_version'] = (string) ($options['http_version'] ?? '') ?: null;
|
||||||
$options['timeout'] = (float) ($options['timeout'] ?? ini_get('default_socket_timeout'));
|
$options['timeout'] = (float) ($options['timeout'] ?? ini_get('default_socket_timeout'));
|
||||||
|
|
||||||
return [$url, $options];
|
return [$url, $options];
|
||||||
@ -128,6 +128,8 @@ trait HttpClientTrait
|
|||||||
*/
|
*/
|
||||||
private static function mergeDefaultOptions(array $options, array $defaultOptions, bool $allowExtraOptions = false): array
|
private static function mergeDefaultOptions(array $options, array $defaultOptions, bool $allowExtraOptions = false): array
|
||||||
{
|
{
|
||||||
|
unset($options['raw_headers'], $defaultOptions['raw_headers']);
|
||||||
|
|
||||||
$options['headers'] = self::normalizeHeaders($options['headers'] ?? []);
|
$options['headers'] = self::normalizeHeaders($options['headers'] ?? []);
|
||||||
|
|
||||||
if ($defaultOptions['headers'] ?? false) {
|
if ($defaultOptions['headers'] ?? false) {
|
||||||
|
85
src/Symfony/Component/HttpClient/MockHttpClient.php
Normal file
85
src/Symfony/Component/HttpClient/MockHttpClient.php
Normal file
@ -0,0 +1,85 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
/*
|
||||||
|
* This file is part of the Symfony package.
|
||||||
|
*
|
||||||
|
* (c) Fabien Potencier <fabien@symfony.com>
|
||||||
|
*
|
||||||
|
* For the full copyright and license information, please view the LICENSE
|
||||||
|
* file that was distributed with this source code.
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace Symfony\Component\HttpClient;
|
||||||
|
|
||||||
|
use Symfony\Component\HttpClient\Exception\TransportException;
|
||||||
|
use Symfony\Component\HttpClient\Response\MockResponse;
|
||||||
|
use Symfony\Component\HttpClient\Response\ResponseStream;
|
||||||
|
use Symfony\Contracts\HttpClient\HttpClientInterface;
|
||||||
|
use Symfony\Contracts\HttpClient\ResponseInterface;
|
||||||
|
use Symfony\Contracts\HttpClient\ResponseStreamInterface;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A test-friendly HttpClient that doesn't make actual HTTP requests.
|
||||||
|
*
|
||||||
|
* @author Nicolas Grekas <p@tchwork.com>
|
||||||
|
*/
|
||||||
|
class MockHttpClient implements HttpClientInterface
|
||||||
|
{
|
||||||
|
use HttpClientTrait;
|
||||||
|
|
||||||
|
private $responseFactory;
|
||||||
|
private $baseUri;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param callable|ResponseInterface|ResponseInterface[]|iterable $responseFactory
|
||||||
|
*/
|
||||||
|
public function __construct($responseFactory, string $baseUri = null)
|
||||||
|
{
|
||||||
|
if ($responseFactory instanceof ResponseInterface) {
|
||||||
|
$responseFactory = [$responseFactory];
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!\is_callable($responseFactory) && !$responseFactory instanceof \Iterator) {
|
||||||
|
$responseFactory = (function () use ($responseFactory) {
|
||||||
|
yield from $responseFactory;
|
||||||
|
})();
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->responseFactory = $responseFactory;
|
||||||
|
$this->baseUri = $baseUri;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
*/
|
||||||
|
public function request(string $method, string $url, array $options = []): ResponseInterface
|
||||||
|
{
|
||||||
|
[$url, $options] = $this->prepareRequest($method, $url, $options, ['base_uri' => $this->baseUri], true);
|
||||||
|
$url = implode('', $url);
|
||||||
|
|
||||||
|
if (\is_callable($this->responseFactory)) {
|
||||||
|
$response = ($this->responseFactory)($method, $url, $options);
|
||||||
|
} elseif (!$this->responseFactory->valid()) {
|
||||||
|
throw new TransportException('The response factory iterator passed to MockHttpClient is empty.');
|
||||||
|
} else {
|
||||||
|
$response = $this->responseFactory->current();
|
||||||
|
$this->responseFactory->next();
|
||||||
|
}
|
||||||
|
|
||||||
|
return MockResponse::fromRequest($method, $url, $options, $response);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
*/
|
||||||
|
public function stream($responses, float $timeout = null): ResponseStreamInterface
|
||||||
|
{
|
||||||
|
if ($responses instanceof ResponseInterface) {
|
||||||
|
$responses = [$responses];
|
||||||
|
} elseif (!\is_iterable($responses)) {
|
||||||
|
throw new \TypeError(sprintf('%s() expects parameter 1 to be an iterable of MockResponse objects, %s given.', __METHOD__, \is_object($responses) ? \get_class($responses) : \gettype($responses)));
|
||||||
|
}
|
||||||
|
|
||||||
|
return new ResponseStream(MockResponse::stream($responses, $timeout));
|
||||||
|
}
|
||||||
|
}
|
265
src/Symfony/Component/HttpClient/Response/MockResponse.php
Normal file
265
src/Symfony/Component/HttpClient/Response/MockResponse.php
Normal file
@ -0,0 +1,265 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
/*
|
||||||
|
* This file is part of the Symfony package.
|
||||||
|
*
|
||||||
|
* (c) Fabien Potencier <fabien@symfony.com>
|
||||||
|
*
|
||||||
|
* For the full copyright and license information, please view the LICENSE
|
||||||
|
* file that was distributed with this source code.
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace Symfony\Component\HttpClient\Response;
|
||||||
|
|
||||||
|
use Symfony\Component\HttpClient\Chunk\ErrorChunk;
|
||||||
|
use Symfony\Component\HttpClient\Chunk\FirstChunk;
|
||||||
|
use Symfony\Component\HttpClient\Exception\InvalidArgumentException;
|
||||||
|
use Symfony\Component\HttpClient\Exception\TransportException;
|
||||||
|
use Symfony\Contracts\HttpClient\ResponseInterface;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A test-friendly response.
|
||||||
|
*
|
||||||
|
* @author Nicolas Grekas <p@tchwork.com>
|
||||||
|
*/
|
||||||
|
class MockResponse implements ResponseInterface
|
||||||
|
{
|
||||||
|
use ResponseTrait;
|
||||||
|
|
||||||
|
private $body;
|
||||||
|
|
||||||
|
private static $mainMulti;
|
||||||
|
private static $idSequence = 0;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param string|string[]|iterable $body The response body as a string or an iterable of strings,
|
||||||
|
* yielding an empty string simulates a timeout,
|
||||||
|
* exceptions are turned to TransportException
|
||||||
|
*
|
||||||
|
* @see ResponseInterface::getInfo() for possible info, e.g. "raw_headers"
|
||||||
|
*/
|
||||||
|
public function __construct($body = '', array $info = [])
|
||||||
|
{
|
||||||
|
$this->body = \is_iterable($body) ? $body : (string) $body;
|
||||||
|
$this->info = $info + $this->info;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
*/
|
||||||
|
public function getInfo(string $type = null)
|
||||||
|
{
|
||||||
|
return null !== $type ? $this->info[$type] ?? null : $this->info;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
*/
|
||||||
|
protected function close(): void
|
||||||
|
{
|
||||||
|
$this->body = [];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @internal
|
||||||
|
*/
|
||||||
|
public static function fromRequest(string $method, string $url, array $options, ResponseInterface $mock): self
|
||||||
|
{
|
||||||
|
$response = new self([]);
|
||||||
|
$response->id = ++self::$idSequence;
|
||||||
|
$response->content = ($options['buffer'] ?? true) ? fopen('php://temp', 'w+') : null;
|
||||||
|
$response->initializer = static function (self $response) {
|
||||||
|
if (null !== $response->info['error']) {
|
||||||
|
throw new TransportException($response->info['error']);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (\is_array($response->body[0] ?? null)) {
|
||||||
|
// Consume the first chunk if it's not yielded yet
|
||||||
|
self::stream([$response])->current();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
$response->info['redirect_count'] = 0;
|
||||||
|
$response->info['redirect_url'] = null;
|
||||||
|
$response->info['start_time'] = microtime(true);
|
||||||
|
$response->info['http_method'] = $method;
|
||||||
|
$response->info['http_code'] = 0;
|
||||||
|
$response->info['user_data'] = $options['user_data'] ?? null;
|
||||||
|
$response->info['url'] = $url;
|
||||||
|
|
||||||
|
self::writeRequest($response, $options, $mock);
|
||||||
|
$response->body[] = [$options, $mock];
|
||||||
|
|
||||||
|
return $response;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
*/
|
||||||
|
protected static function schedule(self $response, array &$runningResponses): void
|
||||||
|
{
|
||||||
|
if (!$response->id) {
|
||||||
|
throw new InvalidArgumentException('MockResponse instances must be issued by MockHttpClient before processing.');
|
||||||
|
}
|
||||||
|
|
||||||
|
$multi = self::$mainMulti ?? self::$mainMulti = (object) [
|
||||||
|
'handlesActivity' => [],
|
||||||
|
'openHandles' => [],
|
||||||
|
];
|
||||||
|
|
||||||
|
if (!isset($runningResponses[0])) {
|
||||||
|
$runningResponses[0] = [$multi, []];
|
||||||
|
}
|
||||||
|
|
||||||
|
$runningResponses[0][1][$response->id] = $response;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
*/
|
||||||
|
protected static function perform(\stdClass $multi, array &$responses): void
|
||||||
|
{
|
||||||
|
foreach ($responses as $response) {
|
||||||
|
$id = $response->id;
|
||||||
|
|
||||||
|
if (!$response->body) {
|
||||||
|
// Last chunk
|
||||||
|
$multi->handlesActivity[$id][] = null;
|
||||||
|
$multi->handlesActivity[$id][] = null !== $response->info['error'] ? new TransportException($response->info['error']) : null;
|
||||||
|
} elseif (null === $chunk = array_shift($response->body)) {
|
||||||
|
// Last chunk
|
||||||
|
$multi->handlesActivity[$id][] = null;
|
||||||
|
$multi->handlesActivity[$id][] = array_shift($response->body);
|
||||||
|
} elseif (\is_array($chunk)) {
|
||||||
|
// First chunk
|
||||||
|
try {
|
||||||
|
$offset = 0;
|
||||||
|
$chunk[1]->getStatusCode();
|
||||||
|
$response->headers = $chunk[1]->getHeaders(false);
|
||||||
|
$multi->handlesActivity[$id][] = new FirstChunk();
|
||||||
|
self::readResponse($response, $chunk[0], $chunk[1], $offset);
|
||||||
|
} catch (\Throwable $e) {
|
||||||
|
$multi->handlesActivity[$id][] = null;
|
||||||
|
$multi->handlesActivity[$id][] = $e;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// Data or timeout chunk
|
||||||
|
$multi->handlesActivity[$id][] = $chunk;
|
||||||
|
|
||||||
|
if (\is_string($chunk) && null !== $response->content) {
|
||||||
|
// Buffer response body
|
||||||
|
fwrite($response->content, $chunk);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
*/
|
||||||
|
protected static function select(\stdClass $multi, float $timeout): int
|
||||||
|
{
|
||||||
|
return 42;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Simulates sending the request.
|
||||||
|
*/
|
||||||
|
private static function writeRequest(self $response, array $options, ResponseInterface $mock)
|
||||||
|
{
|
||||||
|
$onProgress = $options['on_progress'] ?? static function () {};
|
||||||
|
$response->info += $mock->getInfo() ?: [];
|
||||||
|
|
||||||
|
// simulate "size_upload" if it is set
|
||||||
|
if (isset($response->info['size_upload'])) {
|
||||||
|
$response->info['size_upload'] = 0.0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// simulate "total_time" if it is set
|
||||||
|
if (isset($response->info['total_time'])) {
|
||||||
|
$response->info['total_time'] = microtime(true) - $response->info['start_time'];
|
||||||
|
}
|
||||||
|
|
||||||
|
// "notify" DNS resolution
|
||||||
|
$onProgress(0, 0, $response->info);
|
||||||
|
|
||||||
|
// consume the request body
|
||||||
|
if (\is_resource($body = $options['body'] ?? '')) {
|
||||||
|
$data = stream_get_contents($body);
|
||||||
|
if (isset($response->info['size_upload'])) {
|
||||||
|
$response->info['size_upload'] += \strlen($data);
|
||||||
|
}
|
||||||
|
} elseif ($body instanceof \Closure) {
|
||||||
|
while ('' !== $data = $body(16372)) {
|
||||||
|
if (!\is_string($data)) {
|
||||||
|
throw new TransportException(sprintf('Return value of the "body" option callback must be string, %s returned.', \gettype($data)));
|
||||||
|
}
|
||||||
|
|
||||||
|
// "notify" upload progress
|
||||||
|
if (isset($response->info['size_upload'])) {
|
||||||
|
$response->info['size_upload'] += \strlen($data);
|
||||||
|
}
|
||||||
|
|
||||||
|
$onProgress(0, 0, $response->info);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Simulates reading the response.
|
||||||
|
*/
|
||||||
|
private static function readResponse(self $response, array $options, ResponseInterface $mock, int &$offset)
|
||||||
|
{
|
||||||
|
$onProgress = $options['on_progress'] ?? static function () {};
|
||||||
|
|
||||||
|
// populate info related to headers
|
||||||
|
$info = $mock->getInfo() ?: [];
|
||||||
|
$response->info['http_code'] = ($info['http_code'] ?? 0) ?: $mock->getStatusCode(false) ?: 200;
|
||||||
|
$response->addRawHeaders($info['raw_headers'] ?? [], $response->info, $response->headers);
|
||||||
|
$dlSize = (int) ($response->headers['content-length'][0] ?? 0);
|
||||||
|
|
||||||
|
$response->info = [
|
||||||
|
'start_time' => $response->info['start_time'],
|
||||||
|
'user_data' => $response->info['user_data'],
|
||||||
|
'http_code' => $response->info['http_code'],
|
||||||
|
] + $info + $response->info;
|
||||||
|
|
||||||
|
if (isset($response->info['total_time'])) {
|
||||||
|
$response->info['total_time'] = microtime(true) - $response->info['start_time'];
|
||||||
|
}
|
||||||
|
|
||||||
|
// "notify" headers arrival
|
||||||
|
$onProgress(0, $dlSize, $response->info);
|
||||||
|
|
||||||
|
// cast response body to activity list
|
||||||
|
$body = $mock instanceof self ? $mock->body : $mock->getContent(false);
|
||||||
|
|
||||||
|
if (!\is_string($body)) {
|
||||||
|
foreach ($body as $chunk) {
|
||||||
|
if ('' === $chunk = (string) $chunk) {
|
||||||
|
// simulate a timeout
|
||||||
|
$response->body[] = new ErrorChunk($offset);
|
||||||
|
} else {
|
||||||
|
$response->body[] = $chunk;
|
||||||
|
$offset += \strlen($chunk);
|
||||||
|
// "notify" download progress
|
||||||
|
$onProgress($offset, $dlSize, $response->info);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} elseif ('' !== $body) {
|
||||||
|
$response->body[] = $body;
|
||||||
|
$offset = \strlen($body);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isset($response->info['total_time'])) {
|
||||||
|
$response->info['total_time'] = microtime(true) - $response->info['start_time'];
|
||||||
|
}
|
||||||
|
|
||||||
|
// "notify" completion
|
||||||
|
$onProgress($offset, $dlSize, $response->info);
|
||||||
|
|
||||||
|
if (isset($response->headers['content-length']) && $offset !== $dlSize) {
|
||||||
|
throw new TransportException(sprintf('Transfer closed with %s bytes remaining to read.', $dlSize - $offset));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -147,7 +147,7 @@ final class NativeResponse implements ResponseInterface
|
|||||||
$this->inflate = null;
|
$this->inflate = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
$this->multi->openHandles[$this->id] = [$h, $this->buffer, $this->inflate, &$this->content, $this->onProgress, &$this->remaining, &$this->info];
|
$this->multi->openHandles[$this->id] = [$h, $this->buffer, $this->inflate, $this->content, $this->onProgress, &$this->remaining, &$this->info];
|
||||||
$this->multi->handlesActivity[$this->id] = [new FirstChunk()];
|
$this->multi->handlesActivity[$this->id] = [new FirstChunk()];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -259,9 +259,7 @@ trait ResponseTrait
|
|||||||
foreach ($responses as $j => $response) {
|
foreach ($responses as $j => $response) {
|
||||||
$timeoutMax = $timeout ?? max($timeoutMax, $response->timeout);
|
$timeoutMax = $timeout ?? max($timeoutMax, $response->timeout);
|
||||||
$timeoutMin = min($timeoutMin, $response->timeout, 1);
|
$timeoutMin = min($timeoutMin, $response->timeout, 1);
|
||||||
// ErrorChunk instances will set $didThrow to true when the
|
$chunk = false;
|
||||||
// exception they wrap has been thrown after yielding
|
|
||||||
$chunk = $didThrow = false;
|
|
||||||
|
|
||||||
if (isset($multi->handlesActivity[$j])) {
|
if (isset($multi->handlesActivity[$j])) {
|
||||||
// no-op
|
// no-op
|
||||||
@ -269,7 +267,7 @@ trait ResponseTrait
|
|||||||
unset($responses[$j]);
|
unset($responses[$j]);
|
||||||
continue;
|
continue;
|
||||||
} elseif ($isTimeout) {
|
} elseif ($isTimeout) {
|
||||||
$multi->handlesActivity[$j] = [new ErrorChunk($didThrow, $response->offset)];
|
$multi->handlesActivity[$j] = [new ErrorChunk($response->offset)];
|
||||||
} else {
|
} else {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
@ -293,7 +291,7 @@ trait ResponseTrait
|
|||||||
throw $e;
|
throw $e;
|
||||||
}
|
}
|
||||||
|
|
||||||
$chunk = new ErrorChunk($didThrow, $response->offset, $e);
|
$chunk = new ErrorChunk($response->offset, $e);
|
||||||
} else {
|
} else {
|
||||||
$chunk = new LastChunk($response->offset);
|
$chunk = new LastChunk($response->offset);
|
||||||
}
|
}
|
||||||
@ -310,7 +308,7 @@ trait ResponseTrait
|
|||||||
if ($chunk instanceof FirstChunk && null === $response->initializer) {
|
if ($chunk instanceof FirstChunk && null === $response->initializer) {
|
||||||
// Ensure the HTTP status code is always checked
|
// Ensure the HTTP status code is always checked
|
||||||
$response->getHeaders(true);
|
$response->getHeaders(true);
|
||||||
} elseif ($chunk instanceof ErrorChunk && !$didThrow) {
|
} elseif ($chunk instanceof ErrorChunk && !$chunk->didThrow()) {
|
||||||
// Ensure transport exceptions are always thrown
|
// Ensure transport exceptions are always thrown
|
||||||
$chunk->getContent();
|
$chunk->getContent();
|
||||||
}
|
}
|
||||||
|
@ -20,7 +20,7 @@ use Symfony\Contracts\HttpClient\Test\HttpClientTestCase;
|
|||||||
*/
|
*/
|
||||||
class CurlHttpClientTest extends HttpClientTestCase
|
class CurlHttpClientTest extends HttpClientTestCase
|
||||||
{
|
{
|
||||||
protected function getHttpClient(): HttpClientInterface
|
protected function getHttpClient(string $testCase): HttpClientInterface
|
||||||
{
|
{
|
||||||
return new CurlHttpClient();
|
return new CurlHttpClient();
|
||||||
}
|
}
|
||||||
|
130
src/Symfony/Component/HttpClient/Tests/MockHttpClientTest.php
Normal file
130
src/Symfony/Component/HttpClient/Tests/MockHttpClientTest.php
Normal file
@ -0,0 +1,130 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
/*
|
||||||
|
* This file is part of the Symfony package.
|
||||||
|
*
|
||||||
|
* (c) Fabien Potencier <fabien@symfony.com>
|
||||||
|
*
|
||||||
|
* For the full copyright and license information, please view the LICENSE
|
||||||
|
* file that was distributed with this source code.
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace Symfony\Component\HttpClient\Tests;
|
||||||
|
|
||||||
|
use Symfony\Component\HttpClient\Exception\TransportException;
|
||||||
|
use Symfony\Component\HttpClient\MockHttpClient;
|
||||||
|
use Symfony\Component\HttpClient\NativeHttpClient;
|
||||||
|
use Symfony\Component\HttpClient\Response\MockResponse;
|
||||||
|
use Symfony\Contracts\HttpClient\HttpClientInterface;
|
||||||
|
use Symfony\Contracts\HttpClient\ResponseInterface;
|
||||||
|
use Symfony\Contracts\HttpClient\Test\HttpClientTestCase;
|
||||||
|
|
||||||
|
class MockHttpClientTest extends HttpClientTestCase
|
||||||
|
{
|
||||||
|
protected function getHttpClient(string $testCase): HttpClientInterface
|
||||||
|
{
|
||||||
|
$responses = [];
|
||||||
|
|
||||||
|
$headers = [
|
||||||
|
'Host: localhost:8057',
|
||||||
|
'Content-Type: application/json',
|
||||||
|
];
|
||||||
|
|
||||||
|
$body = '{
|
||||||
|
"SERVER_PROTOCOL": "HTTP/1.1",
|
||||||
|
"SERVER_NAME": "127.0.0.1",
|
||||||
|
"REQUEST_URI": "/",
|
||||||
|
"REQUEST_METHOD": "GET",
|
||||||
|
"HTTP_FOO": "baR",
|
||||||
|
"HTTP_HOST": "localhost:8057"
|
||||||
|
}';
|
||||||
|
|
||||||
|
$client = new NativeHttpClient();
|
||||||
|
|
||||||
|
switch ($testCase) {
|
||||||
|
default:
|
||||||
|
return new MockHttpClient(function (string $method, string $url, array $options) use ($client) {
|
||||||
|
try {
|
||||||
|
// force the request to be completed so that we don't test side effects of the transport
|
||||||
|
$response = $client->request($method, $url, $options);
|
||||||
|
$content = $response->getContent(false);
|
||||||
|
|
||||||
|
return new MockResponse($content, $response->getInfo());
|
||||||
|
} catch (\Throwable $e) {
|
||||||
|
$this->fail($e->getMessage());
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
case 'testUnsupportedOption':
|
||||||
|
$this->markTestSkipped('MockHttpClient accepts any options by default');
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'testChunkedEncoding':
|
||||||
|
$this->markTestSkipped("MockHttpClient doesn't dechunk");
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'testGzipBroken':
|
||||||
|
$this->markTestSkipped("MockHttpClient doesn't unzip");
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'testDestruct':
|
||||||
|
$this->markTestSkipped("MockHttpClient doesn't timeout on destruct");
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'testGetRequest':
|
||||||
|
array_unshift($headers, 'HTTP/1.1 200 OK');
|
||||||
|
$responses[] = new MockResponse($body, ['raw_headers' => $headers]);
|
||||||
|
|
||||||
|
$headers = [
|
||||||
|
'Host: localhost:8057',
|
||||||
|
'Content-Length: 1000',
|
||||||
|
'Content-Type: application/json',
|
||||||
|
];
|
||||||
|
|
||||||
|
$responses[] = new MockResponse($body, ['raw_headers' => $headers]);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'testDnsError':
|
||||||
|
$mock = $this->getMockBuilder(ResponseInterface::class)->getMock();
|
||||||
|
$mock->expects($this->any())
|
||||||
|
->method('getStatusCode')
|
||||||
|
->willThrowException(new TransportException('DSN error'));
|
||||||
|
$mock->expects($this->any())
|
||||||
|
->method('getInfo')
|
||||||
|
->willReturn([]);
|
||||||
|
|
||||||
|
$responses[] = $mock;
|
||||||
|
$responses[] = $mock;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'testBadRequestBody':
|
||||||
|
case 'testOnProgressCancel':
|
||||||
|
case 'testOnProgressError':
|
||||||
|
$responses[] = new MockResponse($body, ['raw_headers' => $headers]);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'testTimeoutOnAccess':
|
||||||
|
$mock = $this->getMockBuilder(ResponseInterface::class)->getMock();
|
||||||
|
$mock->expects($this->any())
|
||||||
|
->method('getHeaders')
|
||||||
|
->willThrowException(new TransportException('Timeout'));
|
||||||
|
|
||||||
|
$responses[] = $mock;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'testResolve':
|
||||||
|
$responses[] = new MockResponse($body, ['raw_headers' => $headers]);
|
||||||
|
$responses[] = new MockResponse($body, ['raw_headers' => $headers]);
|
||||||
|
$responses[] = $client->request('GET', 'http://symfony.com:8057/');
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'testTimeoutOnStream':
|
||||||
|
case 'testUncheckedTimeoutThrows':
|
||||||
|
$body = ['<1>', '', '<2>'];
|
||||||
|
$responses[] = new MockResponse($body, ['raw_headers' => $headers]);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
return new MockHttpClient($responses);
|
||||||
|
}
|
||||||
|
}
|
@ -17,7 +17,7 @@ use Symfony\Contracts\HttpClient\Test\HttpClientTestCase;
|
|||||||
|
|
||||||
class NativeHttpClientTest extends HttpClientTestCase
|
class NativeHttpClientTest extends HttpClientTestCase
|
||||||
{
|
{
|
||||||
protected function getHttpClient(): HttpClientInterface
|
protected function getHttpClient(string $testCase): HttpClientInterface
|
||||||
{
|
{
|
||||||
return new NativeHttpClient();
|
return new NativeHttpClient();
|
||||||
}
|
}
|
||||||
|
@ -31,11 +31,11 @@ abstract class HttpClientTestCase extends TestCase
|
|||||||
TestHttpServer::start();
|
TestHttpServer::start();
|
||||||
}
|
}
|
||||||
|
|
||||||
abstract protected function getHttpClient(): HttpClientInterface;
|
abstract protected function getHttpClient(string $testCase): HttpClientInterface;
|
||||||
|
|
||||||
public function testGetRequest()
|
public function testGetRequest()
|
||||||
{
|
{
|
||||||
$client = $this->getHttpClient();
|
$client = $this->getHttpClient(__FUNCTION__);
|
||||||
$response = $client->request('GET', 'http://localhost:8057', [
|
$response = $client->request('GET', 'http://localhost:8057', [
|
||||||
'headers' => ['Foo' => 'baR'],
|
'headers' => ['Foo' => 'baR'],
|
||||||
'user_data' => $data = new \stdClass(),
|
'user_data' => $data = new \stdClass(),
|
||||||
@ -74,7 +74,7 @@ abstract class HttpClientTestCase extends TestCase
|
|||||||
|
|
||||||
public function testNonBufferedGetRequest()
|
public function testNonBufferedGetRequest()
|
||||||
{
|
{
|
||||||
$client = $this->getHttpClient();
|
$client = $this->getHttpClient(__FUNCTION__);
|
||||||
$response = $client->request('GET', 'http://localhost:8057', [
|
$response = $client->request('GET', 'http://localhost:8057', [
|
||||||
'buffer' => false,
|
'buffer' => false,
|
||||||
'headers' => ['Foo' => 'baR'],
|
'headers' => ['Foo' => 'baR'],
|
||||||
@ -89,7 +89,7 @@ abstract class HttpClientTestCase extends TestCase
|
|||||||
|
|
||||||
public function testUnsupportedOption()
|
public function testUnsupportedOption()
|
||||||
{
|
{
|
||||||
$client = $this->getHttpClient();
|
$client = $this->getHttpClient(__FUNCTION__);
|
||||||
|
|
||||||
$this->expectException(\InvalidArgumentException::class);
|
$this->expectException(\InvalidArgumentException::class);
|
||||||
$client->request('GET', 'http://localhost:8057', [
|
$client->request('GET', 'http://localhost:8057', [
|
||||||
@ -99,7 +99,7 @@ abstract class HttpClientTestCase extends TestCase
|
|||||||
|
|
||||||
public function testHttpVersion()
|
public function testHttpVersion()
|
||||||
{
|
{
|
||||||
$client = $this->getHttpClient();
|
$client = $this->getHttpClient(__FUNCTION__);
|
||||||
$response = $client->request('GET', 'http://localhost:8057', [
|
$response = $client->request('GET', 'http://localhost:8057', [
|
||||||
'http_version' => 1.0,
|
'http_version' => 1.0,
|
||||||
]);
|
]);
|
||||||
@ -116,7 +116,7 @@ abstract class HttpClientTestCase extends TestCase
|
|||||||
|
|
||||||
public function testChunkedEncoding()
|
public function testChunkedEncoding()
|
||||||
{
|
{
|
||||||
$client = $this->getHttpClient();
|
$client = $this->getHttpClient(__FUNCTION__);
|
||||||
$response = $client->request('GET', 'http://localhost:8057/chunked');
|
$response = $client->request('GET', 'http://localhost:8057/chunked');
|
||||||
|
|
||||||
$this->assertSame(['chunked'], $response->getHeaders()['transfer-encoding']);
|
$this->assertSame(['chunked'], $response->getHeaders()['transfer-encoding']);
|
||||||
@ -130,7 +130,7 @@ abstract class HttpClientTestCase extends TestCase
|
|||||||
|
|
||||||
public function testClientError()
|
public function testClientError()
|
||||||
{
|
{
|
||||||
$client = $this->getHttpClient();
|
$client = $this->getHttpClient(__FUNCTION__);
|
||||||
$response = $client->request('GET', 'http://localhost:8057/404');
|
$response = $client->request('GET', 'http://localhost:8057/404');
|
||||||
|
|
||||||
$client->stream($response)->valid();
|
$client->stream($response)->valid();
|
||||||
@ -156,7 +156,7 @@ abstract class HttpClientTestCase extends TestCase
|
|||||||
|
|
||||||
public function testIgnoreErrors()
|
public function testIgnoreErrors()
|
||||||
{
|
{
|
||||||
$client = $this->getHttpClient();
|
$client = $this->getHttpClient(__FUNCTION__);
|
||||||
$response = $client->request('GET', 'http://localhost:8057/404');
|
$response = $client->request('GET', 'http://localhost:8057/404');
|
||||||
|
|
||||||
$this->assertSame(404, $response->getStatusCode());
|
$this->assertSame(404, $response->getStatusCode());
|
||||||
@ -164,7 +164,7 @@ abstract class HttpClientTestCase extends TestCase
|
|||||||
|
|
||||||
public function testDnsError()
|
public function testDnsError()
|
||||||
{
|
{
|
||||||
$client = $this->getHttpClient();
|
$client = $this->getHttpClient(__FUNCTION__);
|
||||||
$response = $client->request('GET', 'http://localhost:8057/301/bad-tld');
|
$response = $client->request('GET', 'http://localhost:8057/301/bad-tld');
|
||||||
|
|
||||||
try {
|
try {
|
||||||
@ -201,7 +201,7 @@ abstract class HttpClientTestCase extends TestCase
|
|||||||
|
|
||||||
public function testInlineAuth()
|
public function testInlineAuth()
|
||||||
{
|
{
|
||||||
$client = $this->getHttpClient();
|
$client = $this->getHttpClient(__FUNCTION__);
|
||||||
$response = $client->request('GET', 'http://foo:bar%3Dbar@localhost:8057');
|
$response = $client->request('GET', 'http://foo:bar%3Dbar@localhost:8057');
|
||||||
|
|
||||||
$body = $response->toArray();
|
$body = $response->toArray();
|
||||||
@ -210,9 +210,22 @@ abstract class HttpClientTestCase extends TestCase
|
|||||||
$this->assertSame('bar=bar', $body['PHP_AUTH_PW']);
|
$this->assertSame('bar=bar', $body['PHP_AUTH_PW']);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function testBadRequestBody()
|
||||||
|
{
|
||||||
|
$client = $this->getHttpClient(__FUNCTION__);
|
||||||
|
|
||||||
|
$this->expectException(TransportExceptionInterface::class);
|
||||||
|
|
||||||
|
$response = $client->request('POST', 'http://localhost:8057/', [
|
||||||
|
'body' => function () { yield []; },
|
||||||
|
]);
|
||||||
|
|
||||||
|
$response->getStatusCode();
|
||||||
|
}
|
||||||
|
|
||||||
public function testRedirects()
|
public function testRedirects()
|
||||||
{
|
{
|
||||||
$client = $this->getHttpClient();
|
$client = $this->getHttpClient(__FUNCTION__);
|
||||||
$response = $client->request('POST', 'http://localhost:8057/301', [
|
$response = $client->request('POST', 'http://localhost:8057/301', [
|
||||||
'auth_basic' => 'foo:bar',
|
'auth_basic' => 'foo:bar',
|
||||||
'body' => function () {
|
'body' => function () {
|
||||||
@ -248,7 +261,7 @@ abstract class HttpClientTestCase extends TestCase
|
|||||||
|
|
||||||
public function testRelativeRedirects()
|
public function testRelativeRedirects()
|
||||||
{
|
{
|
||||||
$client = $this->getHttpClient();
|
$client = $this->getHttpClient(__FUNCTION__);
|
||||||
$response = $client->request('GET', 'http://localhost:8057/302/relative');
|
$response = $client->request('GET', 'http://localhost:8057/302/relative');
|
||||||
|
|
||||||
$body = $response->toArray();
|
$body = $response->toArray();
|
||||||
@ -266,7 +279,7 @@ abstract class HttpClientTestCase extends TestCase
|
|||||||
|
|
||||||
public function testRedirect307()
|
public function testRedirect307()
|
||||||
{
|
{
|
||||||
$client = $this->getHttpClient();
|
$client = $this->getHttpClient(__FUNCTION__);
|
||||||
|
|
||||||
$response = $client->request('POST', 'http://localhost:8057/307', [
|
$response = $client->request('POST', 'http://localhost:8057/307', [
|
||||||
'body' => function () {
|
'body' => function () {
|
||||||
@ -288,7 +301,7 @@ abstract class HttpClientTestCase extends TestCase
|
|||||||
|
|
||||||
public function testMaxRedirects()
|
public function testMaxRedirects()
|
||||||
{
|
{
|
||||||
$client = $this->getHttpClient();
|
$client = $this->getHttpClient(__FUNCTION__);
|
||||||
$response = $client->request('GET', 'http://localhost:8057/301', [
|
$response = $client->request('GET', 'http://localhost:8057/301', [
|
||||||
'max_redirects' => 1,
|
'max_redirects' => 1,
|
||||||
'auth_basic' => 'foo:bar',
|
'auth_basic' => 'foo:bar',
|
||||||
@ -322,7 +335,7 @@ abstract class HttpClientTestCase extends TestCase
|
|||||||
|
|
||||||
public function testStream()
|
public function testStream()
|
||||||
{
|
{
|
||||||
$client = $this->getHttpClient();
|
$client = $this->getHttpClient(__FUNCTION__);
|
||||||
|
|
||||||
$response = $client->request('GET', 'http://localhost:8057');
|
$response = $client->request('GET', 'http://localhost:8057');
|
||||||
$chunks = $client->stream($response);
|
$chunks = $client->stream($response);
|
||||||
@ -354,7 +367,7 @@ abstract class HttpClientTestCase extends TestCase
|
|||||||
|
|
||||||
public function testAddToStream()
|
public function testAddToStream()
|
||||||
{
|
{
|
||||||
$client = $this->getHttpClient();
|
$client = $this->getHttpClient(__FUNCTION__);
|
||||||
|
|
||||||
$r1 = $client->request('GET', 'http://localhost:8057');
|
$r1 = $client->request('GET', 'http://localhost:8057');
|
||||||
|
|
||||||
@ -385,7 +398,7 @@ abstract class HttpClientTestCase extends TestCase
|
|||||||
|
|
||||||
public function testCompleteTypeError()
|
public function testCompleteTypeError()
|
||||||
{
|
{
|
||||||
$client = $this->getHttpClient();
|
$client = $this->getHttpClient(__FUNCTION__);
|
||||||
|
|
||||||
$this->expectException(\TypeError::class);
|
$this->expectException(\TypeError::class);
|
||||||
$client->stream(123);
|
$client->stream(123);
|
||||||
@ -393,7 +406,7 @@ abstract class HttpClientTestCase extends TestCase
|
|||||||
|
|
||||||
public function testOnProgress()
|
public function testOnProgress()
|
||||||
{
|
{
|
||||||
$client = $this->getHttpClient();
|
$client = $this->getHttpClient(__FUNCTION__);
|
||||||
$response = $client->request('POST', 'http://localhost:8057/post', [
|
$response = $client->request('POST', 'http://localhost:8057/post', [
|
||||||
'headers' => ['Content-Length' => 14],
|
'headers' => ['Content-Length' => 14],
|
||||||
'body' => 'foo=0123456789',
|
'body' => 'foo=0123456789',
|
||||||
@ -411,7 +424,7 @@ abstract class HttpClientTestCase extends TestCase
|
|||||||
|
|
||||||
public function testPostJson()
|
public function testPostJson()
|
||||||
{
|
{
|
||||||
$client = $this->getHttpClient();
|
$client = $this->getHttpClient(__FUNCTION__);
|
||||||
|
|
||||||
$response = $client->request('POST', 'http://localhost:8057/post', [
|
$response = $client->request('POST', 'http://localhost:8057/post', [
|
||||||
'json' => ['foo' => 'bar'],
|
'json' => ['foo' => 'bar'],
|
||||||
@ -426,7 +439,7 @@ abstract class HttpClientTestCase extends TestCase
|
|||||||
|
|
||||||
public function testPostArray()
|
public function testPostArray()
|
||||||
{
|
{
|
||||||
$client = $this->getHttpClient();
|
$client = $this->getHttpClient(__FUNCTION__);
|
||||||
|
|
||||||
$response = $client->request('POST', 'http://localhost:8057/post', [
|
$response = $client->request('POST', 'http://localhost:8057/post', [
|
||||||
'body' => ['foo' => 'bar'],
|
'body' => ['foo' => 'bar'],
|
||||||
@ -437,7 +450,7 @@ abstract class HttpClientTestCase extends TestCase
|
|||||||
|
|
||||||
public function testPostResource()
|
public function testPostResource()
|
||||||
{
|
{
|
||||||
$client = $this->getHttpClient();
|
$client = $this->getHttpClient(__FUNCTION__);
|
||||||
|
|
||||||
$h = fopen('php://temp', 'w+');
|
$h = fopen('php://temp', 'w+');
|
||||||
fwrite($h, 'foo=0123456789');
|
fwrite($h, 'foo=0123456789');
|
||||||
@ -454,7 +467,7 @@ abstract class HttpClientTestCase extends TestCase
|
|||||||
|
|
||||||
public function testPostCallback()
|
public function testPostCallback()
|
||||||
{
|
{
|
||||||
$client = $this->getHttpClient();
|
$client = $this->getHttpClient(__FUNCTION__);
|
||||||
|
|
||||||
$response = $client->request('POST', 'http://localhost:8057/post', [
|
$response = $client->request('POST', 'http://localhost:8057/post', [
|
||||||
'body' => function () {
|
'body' => function () {
|
||||||
@ -470,7 +483,7 @@ abstract class HttpClientTestCase extends TestCase
|
|||||||
|
|
||||||
public function testOnProgressCancel()
|
public function testOnProgressCancel()
|
||||||
{
|
{
|
||||||
$client = $this->getHttpClient();
|
$client = $this->getHttpClient(__FUNCTION__);
|
||||||
$response = $client->request('GET', 'http://localhost:8057/timeout-body', [
|
$response = $client->request('GET', 'http://localhost:8057/timeout-body', [
|
||||||
'on_progress' => function ($dlNow) {
|
'on_progress' => function ($dlNow) {
|
||||||
if (0 < $dlNow) {
|
if (0 < $dlNow) {
|
||||||
@ -494,7 +507,7 @@ abstract class HttpClientTestCase extends TestCase
|
|||||||
|
|
||||||
public function testOnProgressError()
|
public function testOnProgressError()
|
||||||
{
|
{
|
||||||
$client = $this->getHttpClient();
|
$client = $this->getHttpClient(__FUNCTION__);
|
||||||
$response = $client->request('GET', 'http://localhost:8057/timeout-body', [
|
$response = $client->request('GET', 'http://localhost:8057/timeout-body', [
|
||||||
'on_progress' => function ($dlNow) {
|
'on_progress' => function ($dlNow) {
|
||||||
if (0 < $dlNow) {
|
if (0 < $dlNow) {
|
||||||
@ -518,7 +531,7 @@ abstract class HttpClientTestCase extends TestCase
|
|||||||
|
|
||||||
public function testResolve()
|
public function testResolve()
|
||||||
{
|
{
|
||||||
$client = $this->getHttpClient();
|
$client = $this->getHttpClient(__FUNCTION__);
|
||||||
$response = $client->request('GET', 'http://symfony.com:8057/', [
|
$response = $client->request('GET', 'http://symfony.com:8057/', [
|
||||||
'resolve' => ['symfony.com' => '127.0.0.1'],
|
'resolve' => ['symfony.com' => '127.0.0.1'],
|
||||||
]);
|
]);
|
||||||
@ -533,7 +546,7 @@ abstract class HttpClientTestCase extends TestCase
|
|||||||
|
|
||||||
public function testTimeoutOnAccess()
|
public function testTimeoutOnAccess()
|
||||||
{
|
{
|
||||||
$client = $this->getHttpClient();
|
$client = $this->getHttpClient(__FUNCTION__);
|
||||||
$response = $client->request('GET', 'http://localhost:8057/timeout-header', [
|
$response = $client->request('GET', 'http://localhost:8057/timeout-header', [
|
||||||
'timeout' => 0.1,
|
'timeout' => 0.1,
|
||||||
]);
|
]);
|
||||||
@ -545,7 +558,7 @@ abstract class HttpClientTestCase extends TestCase
|
|||||||
public function testTimeoutOnStream()
|
public function testTimeoutOnStream()
|
||||||
{
|
{
|
||||||
usleep(300000); // wait for the previous test to release the server
|
usleep(300000); // wait for the previous test to release the server
|
||||||
$client = $this->getHttpClient();
|
$client = $this->getHttpClient(__FUNCTION__);
|
||||||
$response = $client->request('GET', 'http://localhost:8057/timeout-body');
|
$response = $client->request('GET', 'http://localhost:8057/timeout-body');
|
||||||
|
|
||||||
$this->assertSame(200, $response->getStatusCode());
|
$this->assertSame(200, $response->getStatusCode());
|
||||||
@ -577,7 +590,7 @@ abstract class HttpClientTestCase extends TestCase
|
|||||||
|
|
||||||
public function testUncheckedTimeoutThrows()
|
public function testUncheckedTimeoutThrows()
|
||||||
{
|
{
|
||||||
$client = $this->getHttpClient();
|
$client = $this->getHttpClient(__FUNCTION__);
|
||||||
$response = $client->request('GET', 'http://localhost:8057/timeout-body');
|
$response = $client->request('GET', 'http://localhost:8057/timeout-body');
|
||||||
$chunks = $client->stream([$response], 0.1);
|
$chunks = $client->stream([$response], 0.1);
|
||||||
|
|
||||||
@ -589,7 +602,7 @@ abstract class HttpClientTestCase extends TestCase
|
|||||||
|
|
||||||
public function testDestruct()
|
public function testDestruct()
|
||||||
{
|
{
|
||||||
$client = $this->getHttpClient();
|
$client = $this->getHttpClient(__FUNCTION__);
|
||||||
|
|
||||||
$downloaded = 0;
|
$downloaded = 0;
|
||||||
$start = microtime(true);
|
$start = microtime(true);
|
||||||
@ -603,7 +616,7 @@ abstract class HttpClientTestCase extends TestCase
|
|||||||
|
|
||||||
public function testProxy()
|
public function testProxy()
|
||||||
{
|
{
|
||||||
$client = $this->getHttpClient();
|
$client = $this->getHttpClient(__FUNCTION__);
|
||||||
$response = $client->request('GET', 'http://localhost:8057/', [
|
$response = $client->request('GET', 'http://localhost:8057/', [
|
||||||
'proxy' => 'http://localhost:8057',
|
'proxy' => 'http://localhost:8057',
|
||||||
]);
|
]);
|
||||||
@ -625,7 +638,7 @@ abstract class HttpClientTestCase extends TestCase
|
|||||||
putenv('no_proxy='.$_SERVER['no_proxy'] = 'example.com, localhost');
|
putenv('no_proxy='.$_SERVER['no_proxy'] = 'example.com, localhost');
|
||||||
|
|
||||||
try {
|
try {
|
||||||
$client = $this->getHttpClient();
|
$client = $this->getHttpClient(__FUNCTION__);
|
||||||
$response = $client->request('GET', 'http://localhost:8057/', [
|
$response = $client->request('GET', 'http://localhost:8057/', [
|
||||||
'proxy' => 'http://localhost:8057',
|
'proxy' => 'http://localhost:8057',
|
||||||
]);
|
]);
|
||||||
@ -646,7 +659,7 @@ abstract class HttpClientTestCase extends TestCase
|
|||||||
*/
|
*/
|
||||||
public function testAutoEncodingRequest()
|
public function testAutoEncodingRequest()
|
||||||
{
|
{
|
||||||
$client = $this->getHttpClient();
|
$client = $this->getHttpClient(__FUNCTION__);
|
||||||
$response = $client->request('GET', 'http://localhost:8057');
|
$response = $client->request('GET', 'http://localhost:8057');
|
||||||
|
|
||||||
$this->assertSame(200, $response->getStatusCode());
|
$this->assertSame(200, $response->getStatusCode());
|
||||||
@ -663,7 +676,7 @@ abstract class HttpClientTestCase extends TestCase
|
|||||||
|
|
||||||
public function testBaseUri()
|
public function testBaseUri()
|
||||||
{
|
{
|
||||||
$client = $this->getHttpClient();
|
$client = $this->getHttpClient(__FUNCTION__);
|
||||||
$response = $client->request('GET', '../404', [
|
$response = $client->request('GET', '../404', [
|
||||||
'base_uri' => 'http://localhost:8057/abc/',
|
'base_uri' => 'http://localhost:8057/abc/',
|
||||||
]);
|
]);
|
||||||
@ -674,7 +687,7 @@ abstract class HttpClientTestCase extends TestCase
|
|||||||
|
|
||||||
public function testQuery()
|
public function testQuery()
|
||||||
{
|
{
|
||||||
$client = $this->getHttpClient();
|
$client = $this->getHttpClient(__FUNCTION__);
|
||||||
$response = $client->request('GET', 'http://localhost:8057/?a=a', [
|
$response = $client->request('GET', 'http://localhost:8057/?a=a', [
|
||||||
'query' => ['b' => 'b'],
|
'query' => ['b' => 'b'],
|
||||||
]);
|
]);
|
||||||
@ -689,7 +702,7 @@ abstract class HttpClientTestCase extends TestCase
|
|||||||
*/
|
*/
|
||||||
public function testUserlandEncodingRequest()
|
public function testUserlandEncodingRequest()
|
||||||
{
|
{
|
||||||
$client = $this->getHttpClient();
|
$client = $this->getHttpClient(__FUNCTION__);
|
||||||
$response = $client->request('GET', 'http://localhost:8057', [
|
$response = $client->request('GET', 'http://localhost:8057', [
|
||||||
'headers' => ['Accept-Encoding' => 'gzip'],
|
'headers' => ['Accept-Encoding' => 'gzip'],
|
||||||
]);
|
]);
|
||||||
@ -711,7 +724,7 @@ abstract class HttpClientTestCase extends TestCase
|
|||||||
*/
|
*/
|
||||||
public function testGzipBroken()
|
public function testGzipBroken()
|
||||||
{
|
{
|
||||||
$client = $this->getHttpClient();
|
$client = $this->getHttpClient(__FUNCTION__);
|
||||||
$response = $client->request('GET', 'http://localhost:8057/gzip-broken');
|
$response = $client->request('GET', 'http://localhost:8057/gzip-broken');
|
||||||
|
|
||||||
$this->expectException(TransportExceptionInterface::class);
|
$this->expectException(TransportExceptionInterface::class);
|
||||||
|
Reference in New Issue
Block a user