Skip to content

Commit ebfd618

Browse files
Handle push cancellation
1 parent ee0f08d commit ebfd618

File tree

3 files changed

+29
-11
lines changed

3 files changed

+29
-11
lines changed

src/Symfony/Component/HttpClient/AmpHttpClient.php

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
namespace Symfony\Component\HttpClient;
1313

1414
use Amp\CancelledException;
15+
use Amp\DeferredFuture;
1516
use Amp\Http\Client\DelegateHttpClient;
1617
use Amp\Http\Client\InterceptedHttpClient;
1718
use Amp\Http\Client\PooledHttpClient;
@@ -154,8 +155,9 @@ public function reset(): void
154155
$this->multi->dnsCache = [];
155156

156157
foreach ($this->multi->pushedResponses as $authority => $pushedResponses) {
157-
foreach ($pushedResponses as [$pushedUrl, $pushedResponse]) {
158-
$pushedResponse->error(new CancelledException());
158+
foreach ($pushedResponses as [$pushedUrl, $pushDeferred]) {
159+
/** @var DeferredFuture $pushDeferred */
160+
$pushDeferred->error(new CancelledException());
159161

160162
$this->logger?->debug(sprintf('Unused pushed response: "%s"', $pushedUrl));
161163
}

src/Symfony/Component/HttpClient/Internal/AmpClientState.php

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
namespace Symfony\Component\HttpClient\Internal;
1313

1414
use Amp\Cancellation;
15+
use Amp\DeferredFuture;
1516
use Amp\Future;
1617
use Amp\Http\Client\Connection\ConnectionLimitingPool;
1718
use Amp\Http\Client\Connection\DefaultConnectionFactory;
@@ -179,6 +180,7 @@ public function connect(SocketAddress|string $uri, ConnectContext $context = nul
179180

180181
private function handlePush(Request $request, Future $response, array $options): void
181182
{
183+
$deferred = new DeferredFuture();
182184
$authority = $request->getUri()->getAuthority();
183185

184186
if ($this->maxPendingPushes <= \count($this->pushedResponses[$authority] ?? [])) {
@@ -189,11 +191,13 @@ private function handlePush(Request $request, Future $response, array $options):
189191

190192
$url = (string) $request->getUri();
191193
$this->logger?->debug(sprintf('Queueing pushed response: "%s"', $url));
192-
$this->pushedResponses[$authority][] = [$url, $response, $request, [
194+
$this->pushedResponses[$authority][] = [$url, $deferred, $request, $response, [
193195
'proxy' => $options['proxy'],
194196
'bindto' => $options['bindto'],
195197
'local_cert' => $options['local_cert'],
196198
'local_pk' => $options['local_pk'],
197199
]];
200+
201+
$deferred->getFuture()->await();
198202
}
199203
}

src/Symfony/Component/HttpClient/Response/AmpResponse.php

Lines changed: 20 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -13,12 +13,12 @@
1313

1414
use Amp\ByteStream\StreamException;
1515
use Amp\DeferredCancellation;
16+
use Amp\DeferredFuture;
17+
use Amp\Future;
1618
use Amp\Http\Client\HttpException;
1719
use Amp\Http\Client\Request;
1820
use Amp\Http\Client\Response;
1921
use Psr\Log\LoggerInterface;
20-
use Revolt\EventLoop;
21-
use Revolt\EventLoop\Suspension;
2222
use Symfony\Component\HttpClient\Chunk\FirstChunk;
2323
use Symfony\Component\HttpClient\Chunk\InformationalChunk;
2424
use Symfony\Component\HttpClient\Exception\InvalidArgumentException;
@@ -196,7 +196,7 @@ private static function generateResponse(Request $request, AmpClientState $multi
196196
});
197197

198198
try {
199-
if (null === $response = self::getPushedResponse($request, $multi, $info, $headers, $options, $logger)) {
199+
if (null === $response = self::getPushedResponse($request, $multi, $info, $headers, $canceller, $options, $logger)) {
200200
$logger?->info(sprintf('Request: "%s %s"', $info['http_method'], $info['url']));
201201

202202
$response = self::followRedirects($request, $multi, $info, $headers, $canceller, $options, $onProgress, $handle, $logger, $pause);
@@ -364,15 +364,16 @@ private static function addResponseHeaders(Response $response, array &$info, arr
364364
/**
365365
* Accepts pushed responses only if their headers related to authentication match the request.
366366
*/
367-
private static function getPushedResponse(Request $request, AmpClientState $multi, array &$info, array &$headers, array $options, ?LoggerInterface $logger): ?Response
367+
private static function getPushedResponse(Request $request, AmpClientState $multi, array &$info, array &$headers, DeferredCancellation $canceller, array $options, ?LoggerInterface $logger): ?Response
368368
{
369369
if ('' !== $options['body']) {
370370
return null;
371371
}
372372

373373
$authority = $request->getUri()->getAuthority();
374+
$cancellation = $canceller->getCancellation();
374375

375-
foreach ($multi->pushedResponses[$authority] ?? [] as $i => [$pushedUrl, $pushedResponse, $pushedRequest, $parentOptions]) {
376+
foreach ($multi->pushedResponses[$authority] ?? [] as $i => [$pushedUrl, $pushDeferred, $pushedRequest, $pushedResponse, $parentOptions]) {
376377
if ($info['url'] !== $pushedUrl || $info['http_method'] !== $pushedRequest->getMethod()) {
377378
continue;
378379
}
@@ -383,13 +384,23 @@ private static function getPushedResponse(Request $request, AmpClientState $mult
383384
}
384385
}
385386

387+
/** @var DeferredFuture $pushDeferred */
388+
$id = $cancellation->subscribe(static fn ($e) => $pushDeferred->error($e));
389+
390+
try {
391+
/** @var Future $pushedResponse */
392+
$response = $pushedResponse->await($cancellation);
393+
} finally {
394+
$cancellation->unsubscribe($id);
395+
}
396+
386397
foreach (['authorization', 'cookie', 'range', 'proxy-authorization'] as $k) {
387-
if ($pushedRequest->getHeaderArray($k) !== $request->getHeaderArray($k)) {
398+
if ($response->getHeaderArray($k) !== $request->getHeaderArray($k)) {
388399
continue 2;
389400
}
390401
}
391402

392-
foreach ($pushedResponse->getHeaderArray('vary') as $vary) {
403+
foreach ($response->getHeaderArray('vary') as $vary) {
393404
foreach (preg_split('/\s*+,\s*+/', $vary) as $v) {
394405
if ('*' === $v || ($pushedRequest->getHeaderArray($v) !== $request->getHeaderArray($v) && 'accept-encoding' !== strtolower($v))) {
395406
$logger?->debug(sprintf('Skipping pushed response: "%s"', $info['url']));
@@ -398,8 +409,9 @@ private static function getPushedResponse(Request $request, AmpClientState $mult
398409
}
399410
}
400411

412+
$pushDeferred->complete();
401413
$logger?->debug(sprintf('Accepting pushed response: "%s %s"', $info['http_method'], $info['url']));
402-
self::addResponseHeaders($pushedResponse, $info, $headers);
414+
self::addResponseHeaders($response, $info, $headers);
403415
unset($multi->pushedResponses[$authority][$i]);
404416

405417
if (!$multi->pushedResponses[$authority]) {

0 commit comments

Comments
 (0)