Created
June 3, 2019 12:23
-
-
Save jderusse/20ce9747e7dcefa9515b4bb17eb7ff2a to your computer and use it in GitHub Desktop.
Cancelable HttpClient
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?php | |
$responses = []; | |
$responses[] = $fooResponse = $this->client->request('GET', '/foo'); | |
$responses[] = $barResponse = $this->client->request('GET', '/foo'); | |
$responses[] = $bazResponse = $this->client->request('GET', '/foo'); | |
// ... | |
if (/* ... */) { | |
// cancel pending requests | |
array_walk($responses, function (CancelableResponseInterface $response): void { | |
$response->cancel(); | |
}); | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?php | |
namespace App\HttpClient; | |
class CanceledException extends \RuntimeException | |
{ | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?php | |
namespace App\HttpClient; | |
use Symfony\Contracts\HttpClient\HttpClientInterface; | |
use Symfony\Contracts\HttpClient\ResponseInterface; | |
use Symfony\Contracts\HttpClient\ResponseStreamInterface; | |
class CancellableHttpClient implements CancellableHttpClientInterface | |
{ | |
private $decorated; | |
public function __construct(HttpClientInterface $decorated) | |
{ | |
$this->decorated = $decorated; | |
} | |
public function request(string $method, string $url, array $options = []): ResponseInterface | |
{ | |
$canceler = new ResponseCanceler(); | |
$onProgress = $options['on_progress'] ?? null; | |
$options['on_progress'] = function ($cur, $tot, array $infos) use ($onProgress, $canceler): void { | |
if ($canceler->isCanceled()) { | |
throw new CanceledException(); | |
} | |
$onProgress && $onProgress($cur, $tot, $infos); | |
}; | |
return new CancellableHttpResponse($this->decorated->request($method, $url, $options), $canceler); | |
} | |
public function stream($responses, float $timeout = null): ResponseStreamInterface | |
{ | |
return $this->decorated->stream($responses, $timeout); | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?php | |
namespace App\HttpClient; | |
use Symfony\Contracts\HttpClient\HttpClientInterface; | |
use Symfony\Contracts\HttpClient\ResponseInterface; | |
interface CancellableHttpClientInterface extends HttpClientInterface | |
{ | |
/** | |
* TODO replace this docBlock by typeHint with covariance in php 7.4 | |
* | |
* @return CancellableResponseInterface | |
*/ | |
public function request(string $method, string $url, array $options = []): ResponseInterface; | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?php | |
namespace App\HttpClient; | |
use Symfony\Component\HttpClient\Exception\TransportException; | |
use Symfony\Contracts\HttpClient\Exception\HttpExceptionInterface; | |
use Symfony\Contracts\HttpClient\ResponseInterface; | |
class CancellableHttpResponse implements CancellableResponseInterface | |
{ | |
private $decorated; | |
private $canceler; | |
public function __construct(ResponseInterface $decorated, ResponseCanceler $canceler) | |
{ | |
$this->decorated = $decorated; | |
$this->canceler = $canceler; | |
} | |
public function __destruct() | |
{ | |
try { | |
$this->decorated = null; | |
} catch (HttpExceptionInterface $e) { | |
if (!$this->canceler->isCanceled()) { | |
throw $e; | |
} | |
} catch (TransportException $e) { | |
if (!$e->getPrevious() instanceof CanceledException) { | |
throw $e; | |
} | |
} | |
} | |
public function getStatusCode(): int | |
{ | |
return $this->decorated->getStatusCode(); | |
} | |
public function getHeaders(bool $throw = true): array | |
{ | |
return $this->decorated->getHeaders($throw); | |
} | |
public function getContent(bool $throw = true): string | |
{ | |
return $this->decorated->getContent($throw); | |
} | |
public function toArray(bool $throw = true): array | |
{ | |
return $this->decorated->toArray($throw); | |
} | |
public function getInfo(string $type = null) | |
{ | |
return $this->decorated->getInfo($type); | |
} | |
public function cancel(): void | |
{ | |
$this->canceler->cancel(); | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?php | |
namespace App\HttpClient; | |
use Symfony\Contracts\HttpClient\ResponseInterface; | |
interface CancellableResponseInterface extends ResponseInterface | |
{ | |
public function cancel(): void; | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?php | |
namespace App\HttpClient; | |
class ResponseCanceler | |
{ | |
private $canceled = false; | |
public function cancel(): void | |
{ | |
$this->canceled = true; | |
} | |
public function isCanceled(): bool | |
{ | |
return $this->canceled; | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment