[HttpClient] Fix decorating progress info in AsyncResponse
authorJérémy Derussé <jeremy@derusse.com>
Mon, 19 Oct 2020 16:27:34 +0000 (18:27 +0200)
committerFabien Potencier <fabien@potencier.org>
Wed, 21 Oct 2020 13:24:25 +0000 (15:24 +0200)
src/Symfony/Bundle/FrameworkBundle/DependencyInjection/FrameworkExtension.php
src/Symfony/Component/HttpClient/DataCollector/HttpClientDataCollector.php
src/Symfony/Component/HttpClient/Response/AsyncContext.php
src/Symfony/Component/HttpClient/Response/AsyncResponse.php
src/Symfony/Component/HttpClient/RetryableHttpClient.php
src/Symfony/Component/HttpClient/Tests/AsyncDecoratorTraitTest.php

index 24cce5f..e99bef7 100644 (file)
@@ -2097,7 +2097,7 @@ class FrameworkExtension extends Extension
 
         $container
             ->register($name.'.retryable', RetryableHttpClient::class)
-            ->setDecoratedService($name, null, -10) // lower priority than TraceableHttpClient
+            ->setDecoratedService($name, null, 10) // higher priority than TraceableHttpClient
             ->setArguments([new Reference($name.'.retryable.inner'), $retryStrategy, $options['max_retries'], new Reference('logger')])
             ->addTag('monolog.logger', ['channel' => 'http_client']);
     }
index 7a60bdd..db8bbbd 100644 (file)
@@ -98,6 +98,7 @@ final class HttpClientDataCollector extends DataCollector implements LateDataCol
         $errorCount = 0;
         $baseInfo = [
             'response_headers' => 1,
+            'retry_count' => 1,
             'redirect_count' => 1,
             'redirect_url' => 1,
             'user_data' => 1,
@@ -152,6 +153,11 @@ final class HttpClientDataCollector extends DataCollector implements LateDataCol
                 $content = [];
             }
 
+            if (isset($info['retry_count'])) {
+                $content['retries'] = $info['previous_info'];
+                unset($info['previous_info']);
+            }
+
             $debugInfo = array_diff_key($info, $baseInfo);
             $info = ['info' => $debugInfo] + array_diff_key($info, $debugInfo) + $content;
             unset($traces[$i]['info']); // break PHP reference used by TraceableHttpClient
index b013896..ebadd19 100644 (file)
@@ -151,6 +151,12 @@ final class AsyncContext
     public function replaceRequest(string $method, string $url, array $options = []): ResponseInterface
     {
         $this->info['previous_info'][] = $this->response->getInfo();
+        if (null !== $onProgress = $options['on_progress'] ?? null) {
+            $thisInfo = &$this->info;
+            $options['on_progress'] = static function (int $dlNow, int $dlSize, array $info) use (&$thisInfo, $onProgress) {
+                $onProgress($dlNow, $dlSize, $thisInfo + $info);
+            };
+        }
 
         return $this->response = $this->client->request($method, $url, ['buffer' => false] + $options);
     }
index ab9dd64..0eb1355 100644 (file)
@@ -43,6 +43,13 @@ final class AsyncResponse implements ResponseInterface, StreamableInterface
     {
         $this->client = $client;
         $this->shouldBuffer = $options['buffer'] ?? true;
+
+        if (null !== $onProgress = $options['on_progress'] ?? null) {
+            $thisInfo = &$this->info;
+            $options['on_progress'] = static function (int $dlNow, int $dlSize, array $info) use (&$thisInfo, $onProgress) {
+                $onProgress($dlNow, $dlSize, $thisInfo + $info);
+            };
+        }
         $this->response = $client->request($method, $url, ['buffer' => false] + $options);
         $this->passthru = $passthru;
         $this->initializer = static function (self $response) {
index 6bca91e..387a33f 100644 (file)
@@ -66,7 +66,6 @@ class RetryableHttpClient implements HttpClientInterface
                 }
             } catch (TransportExceptionInterface $exception) {
                 // catch TransportExceptionInterface to send it to the strategy
-                $context->setInfo('retry_count', $retryCount);
             }
             if (null !== $exception) {
                 // always retry request that fail to resolve DNS
@@ -91,8 +90,6 @@ class RetryableHttpClient implements HttpClientInterface
                     }
                 }
             } elseif ($chunk->isFirst()) {
-                $context->setInfo('retry_count', $retryCount);
-
                 if (false === $shouldRetry = $this->strategy->shouldRetry($context, null, null)) {
                     $context->passthru();
                     yield $chunk;
@@ -138,6 +135,7 @@ class RetryableHttpClient implements HttpClientInterface
                 'delay' => $delay,
             ]);
 
+            $context->setInfo('retry_count', $retryCount);
             $context->replaceRequest($method, $url, $options);
             $context->pause($delay / 1000);
 
index 5bbe244..90872ec 100644 (file)
@@ -263,4 +263,23 @@ class AsyncDecoratorTraitTest extends NativeHttpClientTest
 
         $this->assertSame('{"documents":[{"id":"\/json\/1"},{"id":"\/json\/2"},{"id":"\/json\/3"}]}', $content);
     }
+
+    public function testInfoPassToDecorator()
+    {
+        $lastInfo = null;
+        $options = ['on_progress' => function (int $dlNow, int $dlSize, array $info) use (&$lastInfo) {
+            $lastInfo = $info;
+        }];
+        $client = $this->getHttpClient(__FUNCTION__, function (ChunkInterface $chunk, AsyncContext $context) use ($options) {
+            $context->setInfo('foo', 'test');
+            $context->getResponse()->cancel();
+            $context->replaceRequest('GET', 'http://localhost:8057/', $options);
+            $context->passthru();
+        });
+
+        $client->request('GET', 'http://localhost:8057')->getContent();
+        $this->assertArrayHasKey('foo', $lastInfo);
+        $this->assertSame('test', $lastInfo['foo']);
+        $this->assertArrayHasKey('previous_info', $lastInfo);
+    }
 }