"Fossies" - the Fresh Open Source Software Archive

Member "grav/vendor/symfony/contracts/HttpClient/Test/HttpClientTestCase.php" (1 Sep 2020, 31196 Bytes) of package /linux/www/grav-v1.6.27.zip:


As a special service "Fossies" has tried to format the requested source page into HTML format using (guessed) PHP source code syntax highlighting (style: standard) with prefixed line numbers and code folding option. Alternatively you can here view or download the uninterpreted source code file.

    1 <?php
    2 
    3 /*
    4  * This file is part of the Symfony package.
    5  *
    6  * (c) Fabien Potencier <fabien@symfony.com>
    7  *
    8  * For the full copyright and license information, please view the LICENSE
    9  * file that was distributed with this source code.
   10  */
   11 
   12 namespace Symfony\Contracts\HttpClient\Test;
   13 
   14 use PHPUnit\Framework\TestCase;
   15 use Symfony\Contracts\HttpClient\Exception\ClientExceptionInterface;
   16 use Symfony\Contracts\HttpClient\Exception\RedirectionExceptionInterface;
   17 use Symfony\Contracts\HttpClient\Exception\TransportExceptionInterface;
   18 use Symfony\Contracts\HttpClient\HttpClientInterface;
   19 
   20 /**
   21  * A reference test suite for HttpClientInterface implementations.
   22  *
   23  * @experimental in 1.1
   24  */
   25 abstract class HttpClientTestCase extends TestCase
   26 {
   27     private static $server;
   28 
   29     public static function setUpBeforeClass(): void
   30     {
   31         TestHttpServer::start();
   32     }
   33 
   34     abstract protected function getHttpClient(string $testCase): HttpClientInterface;
   35 
   36     public function testGetRequest()
   37     {
   38         $client = $this->getHttpClient(__FUNCTION__);
   39         $response = $client->request('GET', 'http://localhost:8057', [
   40             'headers' => ['Foo' => 'baR'],
   41             'user_data' => $data = new \stdClass(),
   42         ]);
   43 
   44         $this->assertSame([], $response->getInfo('response_headers'));
   45         $this->assertSame($data, $response->getInfo()['user_data']);
   46         $this->assertSame(200, $response->getStatusCode());
   47 
   48         $info = $response->getInfo();
   49         $this->assertNull($info['error']);
   50         $this->assertSame(0, $info['redirect_count']);
   51         $this->assertSame('HTTP/1.1 200 OK', $info['response_headers'][0]);
   52         $this->assertSame('Host: localhost:8057', $info['response_headers'][1]);
   53         $this->assertSame('http://localhost:8057/', $info['url']);
   54 
   55         $headers = $response->getHeaders();
   56 
   57         $this->assertSame('localhost:8057', $headers['host'][0]);
   58         $this->assertSame(['application/json'], $headers['content-type']);
   59 
   60         $body = json_decode($response->getContent(), true);
   61         $this->assertSame($body, $response->toArray());
   62 
   63         $this->assertSame('HTTP/1.1', $body['SERVER_PROTOCOL']);
   64         $this->assertSame('/', $body['REQUEST_URI']);
   65         $this->assertSame('GET', $body['REQUEST_METHOD']);
   66         $this->assertSame('localhost:8057', $body['HTTP_HOST']);
   67         $this->assertSame('baR', $body['HTTP_FOO']);
   68 
   69         $response = $client->request('GET', 'http://localhost:8057/length-broken');
   70 
   71         $this->expectException(TransportExceptionInterface::class);
   72         $response->getContent();
   73     }
   74 
   75     public function testHeadRequest()
   76     {
   77         $client = $this->getHttpClient(__FUNCTION__);
   78         $response = $client->request('HEAD', 'http://localhost:8057/head', [
   79             'headers' => ['Foo' => 'baR'],
   80             'user_data' => $data = new \stdClass(),
   81             'buffer' => false,
   82         ]);
   83 
   84         $this->assertSame([], $response->getInfo('response_headers'));
   85         $this->assertSame(200, $response->getStatusCode());
   86 
   87         $info = $response->getInfo();
   88         $this->assertSame('HTTP/1.1 200 OK', $info['response_headers'][0]);
   89         $this->assertSame('Host: localhost:8057', $info['response_headers'][1]);
   90 
   91         $headers = $response->getHeaders();
   92 
   93         $this->assertSame('localhost:8057', $headers['host'][0]);
   94         $this->assertSame(['application/json'], $headers['content-type']);
   95         $this->assertTrue(0 < $headers['content-length'][0]);
   96 
   97         $this->assertSame('', $response->getContent());
   98     }
   99 
  100     public function testNonBufferedGetRequest()
  101     {
  102         $client = $this->getHttpClient(__FUNCTION__);
  103         $response = $client->request('GET', 'http://localhost:8057', [
  104             'buffer' => false,
  105             'headers' => ['Foo' => 'baR'],
  106         ]);
  107 
  108         $body = $response->toArray();
  109         $this->assertSame('baR', $body['HTTP_FOO']);
  110 
  111         $this->expectException(TransportExceptionInterface::class);
  112         $response->getContent();
  113     }
  114 
  115     public function testBufferSink()
  116     {
  117         $sink = fopen('php://temp', 'w+');
  118         $client = $this->getHttpClient(__FUNCTION__);
  119         $response = $client->request('GET', 'http://localhost:8057', [
  120             'buffer' => $sink,
  121             'headers' => ['Foo' => 'baR'],
  122         ]);
  123 
  124         $body = $response->toArray();
  125         $this->assertSame('baR', $body['HTTP_FOO']);
  126 
  127         rewind($sink);
  128         $sink = stream_get_contents($sink);
  129         $this->assertSame($sink, $response->getContent());
  130     }
  131 
  132     public function testConditionalBuffering()
  133     {
  134         $client = $this->getHttpClient(__FUNCTION__);
  135         $response = $client->request('GET', 'http://localhost:8057');
  136         $firstContent = $response->getContent();
  137         $secondContent = $response->getContent();
  138 
  139         $this->assertSame($firstContent, $secondContent);
  140 
  141         $response = $client->request('GET', 'http://localhost:8057', ['buffer' => function () { return false; }]);
  142         $response->getContent();
  143 
  144         $this->expectException(TransportExceptionInterface::class);
  145         $response->getContent();
  146     }
  147 
  148     public function testReentrantBufferCallback()
  149     {
  150         $client = $this->getHttpClient(__FUNCTION__);
  151 
  152         $response = $client->request('GET', 'http://localhost:8057', ['buffer' => function () use (&$response) {
  153             $response->cancel();
  154 
  155             return true;
  156         }]);
  157 
  158         $this->assertSame(200, $response->getStatusCode());
  159 
  160         $this->expectException(TransportExceptionInterface::class);
  161         $response->getContent();
  162     }
  163 
  164     public function testThrowingBufferCallback()
  165     {
  166         $client = $this->getHttpClient(__FUNCTION__);
  167 
  168         $response = $client->request('GET', 'http://localhost:8057', ['buffer' => function () {
  169             throw new \Exception('Boo');
  170         }]);
  171 
  172         $this->assertSame(200, $response->getStatusCode());
  173 
  174         $this->expectException(TransportExceptionInterface::class);
  175         $this->expectExceptionMessage('Boo');
  176         $response->getContent();
  177     }
  178 
  179     public function testUnsupportedOption()
  180     {
  181         $client = $this->getHttpClient(__FUNCTION__);
  182 
  183         $this->expectException(\InvalidArgumentException::class);
  184         $client->request('GET', 'http://localhost:8057', [
  185             'capture_peer_cert' => 1.0,
  186         ]);
  187     }
  188 
  189     public function testHttpVersion()
  190     {
  191         $client = $this->getHttpClient(__FUNCTION__);
  192         $response = $client->request('GET', 'http://localhost:8057', [
  193             'http_version' => 1.0,
  194         ]);
  195 
  196         $this->assertSame(200, $response->getStatusCode());
  197         $this->assertSame('HTTP/1.0 200 OK', $response->getInfo('response_headers')[0]);
  198 
  199         $body = $response->toArray();
  200 
  201         $this->assertSame('HTTP/1.0', $body['SERVER_PROTOCOL']);
  202         $this->assertSame('GET', $body['REQUEST_METHOD']);
  203         $this->assertSame('/', $body['REQUEST_URI']);
  204     }
  205 
  206     public function testChunkedEncoding()
  207     {
  208         $client = $this->getHttpClient(__FUNCTION__);
  209         $response = $client->request('GET', 'http://localhost:8057/chunked');
  210 
  211         $this->assertSame(['chunked'], $response->getHeaders()['transfer-encoding']);
  212         $this->assertSame('Symfony is awesome!', $response->getContent());
  213 
  214         $response = $client->request('GET', 'http://localhost:8057/chunked-broken');
  215 
  216         $this->expectException(TransportExceptionInterface::class);
  217         $response->getContent();
  218     }
  219 
  220     public function testClientError()
  221     {
  222         $client = $this->getHttpClient(__FUNCTION__);
  223         $response = $client->request('GET', 'http://localhost:8057/404');
  224 
  225         $client->stream($response)->valid();
  226 
  227         $this->assertSame(404, $response->getInfo('http_code'));
  228 
  229         try {
  230             $response->getHeaders();
  231             $this->fail(ClientExceptionInterface::class.' expected');
  232         } catch (ClientExceptionInterface $e) {
  233         }
  234 
  235         try {
  236             $response->getContent();
  237             $this->fail(ClientExceptionInterface::class.' expected');
  238         } catch (ClientExceptionInterface $e) {
  239         }
  240 
  241         $this->assertSame(404, $response->getStatusCode());
  242         $this->assertSame(['application/json'], $response->getHeaders(false)['content-type']);
  243         $this->assertNotEmpty($response->getContent(false));
  244 
  245         $response = $client->request('GET', 'http://localhost:8057/404');
  246 
  247         try {
  248             foreach ($client->stream($response) as $chunk) {
  249                 $this->assertTrue($chunk->isFirst());
  250             }
  251             $this->fail(ClientExceptionInterface::class.' expected');
  252         } catch (ClientExceptionInterface $e) {
  253         }
  254     }
  255 
  256     public function testIgnoreErrors()
  257     {
  258         $client = $this->getHttpClient(__FUNCTION__);
  259         $response = $client->request('GET', 'http://localhost:8057/404');
  260 
  261         $this->assertSame(404, $response->getStatusCode());
  262     }
  263 
  264     public function testDnsError()
  265     {
  266         $client = $this->getHttpClient(__FUNCTION__);
  267         $response = $client->request('GET', 'http://localhost:8057/301/bad-tld');
  268 
  269         try {
  270             $response->getStatusCode();
  271             $this->fail(TransportExceptionInterface::class.' expected');
  272         } catch (TransportExceptionInterface $e) {
  273             $this->addToAssertionCount(1);
  274         }
  275 
  276         try {
  277             $response->getStatusCode();
  278             $this->fail(TransportExceptionInterface::class.' still expected');
  279         } catch (TransportExceptionInterface $e) {
  280             $this->addToAssertionCount(1);
  281         }
  282 
  283         $response = $client->request('GET', 'http://localhost:8057/301/bad-tld');
  284 
  285         try {
  286             foreach ($client->stream($response) as $r => $chunk) {
  287             }
  288             $this->fail(TransportExceptionInterface::class.' expected');
  289         } catch (TransportExceptionInterface $e) {
  290             $this->addToAssertionCount(1);
  291         }
  292 
  293         $this->assertSame($response, $r);
  294         $this->assertNotNull($chunk->getError());
  295 
  296         $this->expectException(TransportExceptionInterface::class);
  297         foreach ($client->stream($response) as $chunk) {
  298         }
  299     }
  300 
  301     public function testInlineAuth()
  302     {
  303         $client = $this->getHttpClient(__FUNCTION__);
  304         $response = $client->request('GET', 'http://foo:bar%3Dbar@localhost:8057');
  305 
  306         $body = $response->toArray();
  307 
  308         $this->assertSame('foo', $body['PHP_AUTH_USER']);
  309         $this->assertSame('bar=bar', $body['PHP_AUTH_PW']);
  310     }
  311 
  312     public function testBadRequestBody()
  313     {
  314         $client = $this->getHttpClient(__FUNCTION__);
  315 
  316         $this->expectException(TransportExceptionInterface::class);
  317 
  318         $response = $client->request('POST', 'http://localhost:8057/', [
  319             'body' => function () { yield []; },
  320         ]);
  321 
  322         $response->getStatusCode();
  323     }
  324 
  325     public function test304()
  326     {
  327         $client = $this->getHttpClient(__FUNCTION__);
  328         $response = $client->request('GET', 'http://localhost:8057/304', [
  329             'headers' => ['If-Match' => '"abc"'],
  330             'buffer' => false,
  331         ]);
  332 
  333         $this->assertSame(304, $response->getStatusCode());
  334         $this->assertSame('', $response->getContent(false));
  335     }
  336 
  337     public function testRedirects()
  338     {
  339         $client = $this->getHttpClient(__FUNCTION__);
  340         $response = $client->request('POST', 'http://localhost:8057/301', [
  341             'auth_basic' => 'foo:bar',
  342             'body' => function () {
  343                 yield 'foo=bar';
  344             },
  345         ]);
  346 
  347         $body = $response->toArray();
  348         $this->assertSame('GET', $body['REQUEST_METHOD']);
  349         $this->assertSame('Basic Zm9vOmJhcg==', $body['HTTP_AUTHORIZATION']);
  350         $this->assertSame('http://localhost:8057/', $response->getInfo('url'));
  351 
  352         $this->assertSame(2, $response->getInfo('redirect_count'));
  353         $this->assertNull($response->getInfo('redirect_url'));
  354 
  355         $expected = [
  356             'HTTP/1.1 301 Moved Permanently',
  357             'Location: http://127.0.0.1:8057/302',
  358             'Content-Type: application/json',
  359             'HTTP/1.1 302 Found',
  360             'Location: http://localhost:8057/',
  361             'Content-Type: application/json',
  362             'HTTP/1.1 200 OK',
  363             'Content-Type: application/json',
  364         ];
  365 
  366         $filteredHeaders = array_values(array_filter($response->getInfo('response_headers'), function ($h) {
  367             return \in_array(substr($h, 0, 4), ['HTTP', 'Loca', 'Cont'], true) && 'Content-Encoding: gzip' !== $h;
  368         }));
  369 
  370         $this->assertSame($expected, $filteredHeaders);
  371     }
  372 
  373     public function testInvalidRedirect()
  374     {
  375         $client = $this->getHttpClient(__FUNCTION__);
  376         $response = $client->request('GET', 'http://localhost:8057/301/invalid');
  377 
  378         $this->assertSame(301, $response->getStatusCode());
  379         $this->assertSame(['//?foo=bar'], $response->getHeaders(false)['location']);
  380         $this->assertSame(0, $response->getInfo('redirect_count'));
  381         $this->assertNull($response->getInfo('redirect_url'));
  382 
  383         $this->expectException(RedirectionExceptionInterface::class);
  384         $response->getHeaders();
  385     }
  386 
  387     public function testRelativeRedirects()
  388     {
  389         $client = $this->getHttpClient(__FUNCTION__);
  390         $response = $client->request('GET', 'http://localhost:8057/302/relative');
  391 
  392         $body = $response->toArray();
  393 
  394         $this->assertSame('/', $body['REQUEST_URI']);
  395         $this->assertNull($response->getInfo('redirect_url'));
  396 
  397         $response = $client->request('GET', 'http://localhost:8057/302/relative', [
  398             'max_redirects' => 0,
  399         ]);
  400 
  401         $this->assertSame(302, $response->getStatusCode());
  402         $this->assertSame('http://localhost:8057/', $response->getInfo('redirect_url'));
  403     }
  404 
  405     public function testRedirect307()
  406     {
  407         $client = $this->getHttpClient(__FUNCTION__);
  408 
  409         $response = $client->request('POST', 'http://localhost:8057/307', [
  410             'body' => function () {
  411                 yield 'foo=bar';
  412             },
  413             'max_redirects' => 0,
  414         ]);
  415 
  416         $this->assertSame(307, $response->getStatusCode());
  417 
  418         $response = $client->request('POST', 'http://localhost:8057/307', [
  419             'body' => 'foo=bar',
  420         ]);
  421 
  422         $body = $response->toArray();
  423 
  424         $this->assertSame(['foo' => 'bar', 'REQUEST_METHOD' => 'POST'], $body);
  425     }
  426 
  427     public function testMaxRedirects()
  428     {
  429         $client = $this->getHttpClient(__FUNCTION__);
  430         $response = $client->request('GET', 'http://localhost:8057/301', [
  431             'max_redirects' => 1,
  432             'auth_basic' => 'foo:bar',
  433         ]);
  434 
  435         try {
  436             $response->getHeaders();
  437             $this->fail(RedirectionExceptionInterface::class.' expected');
  438         } catch (RedirectionExceptionInterface $e) {
  439         }
  440 
  441         $this->assertSame(302, $response->getStatusCode());
  442         $this->assertSame(1, $response->getInfo('redirect_count'));
  443         $this->assertSame('http://localhost:8057/', $response->getInfo('redirect_url'));
  444 
  445         $expected = [
  446             'HTTP/1.1 301 Moved Permanently',
  447             'Location: http://127.0.0.1:8057/302',
  448             'Content-Type: application/json',
  449             'HTTP/1.1 302 Found',
  450             'Location: http://localhost:8057/',
  451             'Content-Type: application/json',
  452         ];
  453 
  454         $filteredHeaders = array_values(array_filter($response->getInfo('response_headers'), function ($h) {
  455             return \in_array(substr($h, 0, 4), ['HTTP', 'Loca', 'Cont'], true);
  456         }));
  457 
  458         $this->assertSame($expected, $filteredHeaders);
  459     }
  460 
  461     public function testStream()
  462     {
  463         $client = $this->getHttpClient(__FUNCTION__);
  464 
  465         $response = $client->request('GET', 'http://localhost:8057');
  466         $chunks = $client->stream($response);
  467         $result = [];
  468 
  469         foreach ($chunks as $r => $chunk) {
  470             if ($chunk->isTimeout()) {
  471                 $result[] = 't';
  472             } elseif ($chunk->isLast()) {
  473                 $result[] = 'l';
  474             } elseif ($chunk->isFirst()) {
  475                 $result[] = 'f';
  476             }
  477         }
  478 
  479         $this->assertSame($response, $r);
  480         $this->assertSame(['f', 'l'], $result);
  481 
  482         $chunk = null;
  483         $i = 0;
  484 
  485         foreach ($client->stream($response) as $chunk) {
  486             ++$i;
  487         }
  488 
  489         $this->assertSame(1, $i);
  490         $this->assertTrue($chunk->isLast());
  491     }
  492 
  493     public function testAddToStream()
  494     {
  495         $client = $this->getHttpClient(__FUNCTION__);
  496 
  497         $r1 = $client->request('GET', 'http://localhost:8057');
  498 
  499         $completed = [];
  500 
  501         $pool = [$r1];
  502 
  503         while ($pool) {
  504             $chunks = $client->stream($pool);
  505             $pool = [];
  506 
  507             foreach ($chunks as $r => $chunk) {
  508                 if (!$chunk->isLast()) {
  509                     continue;
  510                 }
  511 
  512                 if ($r1 === $r) {
  513                     $r2 = $client->request('GET', 'http://localhost:8057');
  514                     $pool[] = $r2;
  515                 }
  516 
  517                 $completed[] = $r;
  518             }
  519         }
  520 
  521         $this->assertSame([$r1, $r2], $completed);
  522     }
  523 
  524     public function testCompleteTypeError()
  525     {
  526         $client = $this->getHttpClient(__FUNCTION__);
  527 
  528         $this->expectException(\TypeError::class);
  529         $client->stream(123);
  530     }
  531 
  532     public function testOnProgress()
  533     {
  534         $client = $this->getHttpClient(__FUNCTION__);
  535         $response = $client->request('POST', 'http://localhost:8057/post', [
  536             'headers' => ['Content-Length' => 14],
  537             'body' => 'foo=0123456789',
  538             'on_progress' => function (...$state) use (&$steps) { $steps[] = $state; },
  539         ]);
  540 
  541         $body = $response->toArray();
  542 
  543         $this->assertSame(['foo' => '0123456789', 'REQUEST_METHOD' => 'POST'], $body);
  544         $this->assertSame([0, 0], \array_slice($steps[0], 0, 2));
  545         $lastStep = \array_slice($steps, -1)[0];
  546         $this->assertSame([57, 57], \array_slice($lastStep, 0, 2));
  547         $this->assertSame('http://localhost:8057/post', $steps[0][2]['url']);
  548     }
  549 
  550     public function testPostJson()
  551     {
  552         $client = $this->getHttpClient(__FUNCTION__);
  553 
  554         $response = $client->request('POST', 'http://localhost:8057/post', [
  555             'json' => ['foo' => 'bar'],
  556         ]);
  557 
  558         $body = $response->toArray();
  559 
  560         $this->assertStringContainsString('json', $body['content-type']);
  561         unset($body['content-type']);
  562         $this->assertSame(['foo' => 'bar', 'REQUEST_METHOD' => 'POST'], $body);
  563     }
  564 
  565     public function testPostArray()
  566     {
  567         $client = $this->getHttpClient(__FUNCTION__);
  568 
  569         $response = $client->request('POST', 'http://localhost:8057/post', [
  570             'body' => ['foo' => 'bar'],
  571         ]);
  572 
  573         $this->assertSame(['foo' => 'bar', 'REQUEST_METHOD' => 'POST'], $response->toArray());
  574     }
  575 
  576     public function testPostResource()
  577     {
  578         $client = $this->getHttpClient(__FUNCTION__);
  579 
  580         $h = fopen('php://temp', 'w+');
  581         fwrite($h, 'foo=0123456789');
  582         rewind($h);
  583 
  584         $response = $client->request('POST', 'http://localhost:8057/post', [
  585             'body' => $h,
  586         ]);
  587 
  588         $body = $response->toArray();
  589 
  590         $this->assertSame(['foo' => '0123456789', 'REQUEST_METHOD' => 'POST'], $body);
  591     }
  592 
  593     public function testPostCallback()
  594     {
  595         $client = $this->getHttpClient(__FUNCTION__);
  596 
  597         $response = $client->request('POST', 'http://localhost:8057/post', [
  598             'body' => function () {
  599                 yield 'foo';
  600                 yield '';
  601                 yield '=';
  602                 yield '0123456789';
  603             },
  604         ]);
  605 
  606         $this->assertSame(['foo' => '0123456789', 'REQUEST_METHOD' => 'POST'], $response->toArray());
  607     }
  608 
  609     public function testCancel()
  610     {
  611         $client = $this->getHttpClient(__FUNCTION__);
  612         $response = $client->request('GET', 'http://localhost:8057/timeout-header');
  613 
  614         $response->cancel();
  615         $this->expectException(TransportExceptionInterface::class);
  616         $response->getHeaders();
  617     }
  618 
  619     public function testInfoOnCanceledResponse()
  620     {
  621         $client = $this->getHttpClient(__FUNCTION__);
  622 
  623         $response = $client->request('GET', 'http://localhost:8057/timeout-header');
  624 
  625         $this->assertFalse($response->getInfo('canceled'));
  626         $response->cancel();
  627         $this->assertTrue($response->getInfo('canceled'));
  628     }
  629 
  630     public function testCancelInStream()
  631     {
  632         $client = $this->getHttpClient(__FUNCTION__);
  633         $response = $client->request('GET', 'http://localhost:8057/404');
  634 
  635         foreach ($client->stream($response) as $chunk) {
  636             $response->cancel();
  637         }
  638 
  639         $this->expectException(TransportExceptionInterface::class);
  640 
  641         foreach ($client->stream($response) as $chunk) {
  642         }
  643     }
  644 
  645     public function testOnProgressCancel()
  646     {
  647         $client = $this->getHttpClient(__FUNCTION__);
  648         $response = $client->request('GET', 'http://localhost:8057/timeout-body', [
  649             'on_progress' => function ($dlNow) {
  650                 if (0 < $dlNow) {
  651                     throw new \Exception('Aborting the request');
  652                 }
  653             },
  654         ]);
  655 
  656         try {
  657             foreach ($client->stream([$response]) as $chunk) {
  658             }
  659             $this->fail(ClientExceptionInterface::class.' expected');
  660         } catch (TransportExceptionInterface $e) {
  661             $this->assertSame('Aborting the request', $e->getPrevious()->getMessage());
  662         }
  663 
  664         $this->assertNotNull($response->getInfo('error'));
  665         $this->expectException(TransportExceptionInterface::class);
  666         $response->getContent();
  667     }
  668 
  669     public function testOnProgressError()
  670     {
  671         $client = $this->getHttpClient(__FUNCTION__);
  672         $response = $client->request('GET', 'http://localhost:8057/timeout-body', [
  673             'on_progress' => function ($dlNow) {
  674                 if (0 < $dlNow) {
  675                     throw new \Error('BUG');
  676                 }
  677             },
  678         ]);
  679 
  680         try {
  681             foreach ($client->stream([$response]) as $chunk) {
  682             }
  683             $this->fail('Error expected');
  684         } catch (\Error $e) {
  685             $this->assertSame('BUG', $e->getMessage());
  686         }
  687 
  688         $this->assertNotNull($response->getInfo('error'));
  689         $this->expectException(TransportExceptionInterface::class);
  690         $response->getContent();
  691     }
  692 
  693     public function testResolve()
  694     {
  695         $client = $this->getHttpClient(__FUNCTION__);
  696         $response = $client->request('GET', 'http://symfony.com:8057/', [
  697             'resolve' => ['symfony.com' => '127.0.0.1'],
  698         ]);
  699 
  700         $this->assertSame(200, $response->getStatusCode());
  701         $this->assertSame(200, $client->request('GET', 'http://symfony.com:8057/')->getStatusCode());
  702 
  703         $response = null;
  704         $this->expectException(TransportExceptionInterface::class);
  705         $client->request('GET', 'http://symfony.com:8057/', ['timeout' => 1]);
  706     }
  707 
  708     public function testNotATimeout()
  709     {
  710         $client = $this->getHttpClient(__FUNCTION__);
  711         $response = $client->request('GET', 'http://localhost:8057/timeout-header', [
  712             'timeout' => 0.9,
  713         ]);
  714         sleep(1);
  715         $this->assertSame(200, $response->getStatusCode());
  716     }
  717 
  718     public function testTimeoutOnAccess()
  719     {
  720         $client = $this->getHttpClient(__FUNCTION__);
  721         $response = $client->request('GET', 'http://localhost:8057/timeout-header', [
  722             'timeout' => 0.1,
  723         ]);
  724 
  725         $this->expectException(TransportExceptionInterface::class);
  726         $response->getHeaders();
  727     }
  728 
  729     public function testTimeoutOnStream()
  730     {
  731         usleep(300000); // wait for the previous test to release the server
  732         $client = $this->getHttpClient(__FUNCTION__);
  733         $response = $client->request('GET', 'http://localhost:8057/timeout-body');
  734 
  735         $this->assertSame(200, $response->getStatusCode());
  736         $chunks = $client->stream([$response], 0.2);
  737 
  738         $result = [];
  739 
  740         foreach ($chunks as $r => $chunk) {
  741             if ($chunk->isTimeout()) {
  742                 $result[] = 't';
  743             } else {
  744                 $result[] = $chunk->getContent();
  745             }
  746         }
  747 
  748         $this->assertSame(['<1>', 't'], $result);
  749 
  750         $chunks = $client->stream([$response]);
  751 
  752         foreach ($chunks as $r => $chunk) {
  753             $this->assertSame('<2>', $chunk->getContent());
  754             $this->assertSame('<1><2>', $r->getContent());
  755 
  756             return;
  757         }
  758 
  759         $this->fail('The response should have completed');
  760     }
  761 
  762     public function testUncheckedTimeoutThrows()
  763     {
  764         $client = $this->getHttpClient(__FUNCTION__);
  765         $response = $client->request('GET', 'http://localhost:8057/timeout-body');
  766         $chunks = $client->stream([$response], 0.1);
  767 
  768         $this->expectException(TransportExceptionInterface::class);
  769 
  770         foreach ($chunks as $r => $chunk) {
  771         }
  772     }
  773 
  774     public function testDestruct()
  775     {
  776         $client = $this->getHttpClient(__FUNCTION__);
  777 
  778         $start = microtime(true);
  779         $client->request('GET', 'http://localhost:8057/timeout-long');
  780         $client = null;
  781         $duration = microtime(true) - $start;
  782 
  783         $this->assertGreaterThan(1, $duration);
  784         $this->assertLessThan(4, $duration);
  785     }
  786 
  787     public function testProxy()
  788     {
  789         $client = $this->getHttpClient(__FUNCTION__);
  790         $response = $client->request('GET', 'http://localhost:8057/', [
  791             'proxy' => 'http://localhost:8057',
  792         ]);
  793 
  794         $body = $response->toArray();
  795         $this->assertSame('localhost:8057', $body['HTTP_HOST']);
  796         $this->assertRegexp('#^http://(localhost|127\.0\.0\.1):8057/$#', $body['REQUEST_URI']);
  797 
  798         $response = $client->request('GET', 'http://localhost:8057/', [
  799             'proxy' => 'http://foo:b%3Dar@localhost:8057',
  800         ]);
  801 
  802         $body = $response->toArray();
  803         $this->assertSame('Basic Zm9vOmI9YXI=', $body['HTTP_PROXY_AUTHORIZATION']);
  804     }
  805 
  806     public function testNoProxy()
  807     {
  808         putenv('no_proxy='.$_SERVER['no_proxy'] = 'example.com, localhost');
  809 
  810         try {
  811             $client = $this->getHttpClient(__FUNCTION__);
  812             $response = $client->request('GET', 'http://localhost:8057/', [
  813                 'proxy' => 'http://localhost:8057',
  814             ]);
  815 
  816             $body = $response->toArray();
  817 
  818             $this->assertSame('HTTP/1.1', $body['SERVER_PROTOCOL']);
  819             $this->assertSame('/', $body['REQUEST_URI']);
  820             $this->assertSame('GET', $body['REQUEST_METHOD']);
  821         } finally {
  822             putenv('no_proxy');
  823             unset($_SERVER['no_proxy']);
  824         }
  825     }
  826 
  827     /**
  828      * @requires extension zlib
  829      */
  830     public function testAutoEncodingRequest()
  831     {
  832         $client = $this->getHttpClient(__FUNCTION__);
  833         $response = $client->request('GET', 'http://localhost:8057');
  834 
  835         $this->assertSame(200, $response->getStatusCode());
  836 
  837         $headers = $response->getHeaders();
  838 
  839         $this->assertSame(['Accept-Encoding'], $headers['vary']);
  840         $this->assertStringContainsString('gzip', $headers['content-encoding'][0]);
  841 
  842         $body = $response->toArray();
  843 
  844         $this->assertStringContainsString('gzip', $body['HTTP_ACCEPT_ENCODING']);
  845     }
  846 
  847     public function testBaseUri()
  848     {
  849         $client = $this->getHttpClient(__FUNCTION__);
  850         $response = $client->request('GET', '../404', [
  851             'base_uri' => 'http://localhost:8057/abc/',
  852         ]);
  853 
  854         $this->assertSame(404, $response->getStatusCode());
  855         $this->assertSame(['application/json'], $response->getHeaders(false)['content-type']);
  856     }
  857 
  858     public function testQuery()
  859     {
  860         $client = $this->getHttpClient(__FUNCTION__);
  861         $response = $client->request('GET', 'http://localhost:8057/?a=a', [
  862             'query' => ['b' => 'b'],
  863         ]);
  864 
  865         $body = $response->toArray();
  866         $this->assertSame('GET', $body['REQUEST_METHOD']);
  867         $this->assertSame('/?a=a&b=b', $body['REQUEST_URI']);
  868     }
  869 
  870     public function testInformationalResponse()
  871     {
  872         $client = $this->getHttpClient(__FUNCTION__);
  873         $response = $client->request('GET', 'http://localhost:8057/103');
  874 
  875         $this->assertSame('Here the body', $response->getContent());
  876         $this->assertSame(200, $response->getStatusCode());
  877     }
  878 
  879     public function testInformationalResponseStream()
  880     {
  881         $client = $this->getHttpClient(__FUNCTION__);
  882         $response = $client->request('GET', 'http://localhost:8057/103');
  883 
  884         $chunks = [];
  885         foreach ($client->stream($response) as $chunk) {
  886             $chunks[] = $chunk;
  887         }
  888 
  889         $this->assertSame(103, $chunks[0]->getInformationalStatus()[0]);
  890         $this->assertSame(['</style.css>; rel=preload; as=style', '</script.js>; rel=preload; as=script'], $chunks[0]->getInformationalStatus()[1]['link']);
  891         $this->assertTrue($chunks[1]->isFirst());
  892         $this->assertSame('Here the body', $chunks[2]->getContent());
  893         $this->assertTrue($chunks[3]->isLast());
  894         $this->assertNull($chunks[3]->getInformationalStatus());
  895 
  896         $this->assertSame(['date', 'content-length'], array_keys($response->getHeaders()));
  897         $this->assertContains('Link: </style.css>; rel=preload; as=style', $response->getInfo('response_headers'));
  898     }
  899 
  900     /**
  901      * @requires extension zlib
  902      */
  903     public function testUserlandEncodingRequest()
  904     {
  905         $client = $this->getHttpClient(__FUNCTION__);
  906         $response = $client->request('GET', 'http://localhost:8057', [
  907             'headers' => ['Accept-Encoding' => 'gzip'],
  908         ]);
  909 
  910         $headers = $response->getHeaders();
  911 
  912         $this->assertSame(['Accept-Encoding'], $headers['vary']);
  913         $this->assertStringContainsString('gzip', $headers['content-encoding'][0]);
  914 
  915         $body = $response->getContent();
  916         $this->assertSame("\x1F", $body[0]);
  917 
  918         $body = json_decode(gzdecode($body), true);
  919         $this->assertSame('gzip', $body['HTTP_ACCEPT_ENCODING']);
  920     }
  921 
  922     /**
  923      * @requires extension zlib
  924      */
  925     public function testGzipBroken()
  926     {
  927         $client = $this->getHttpClient(__FUNCTION__);
  928         $response = $client->request('GET', 'http://localhost:8057/gzip-broken');
  929 
  930         $this->expectException(TransportExceptionInterface::class);
  931         $response->getContent();
  932     }
  933 
  934     public function testMaxDuration()
  935     {
  936         $client = $this->getHttpClient(__FUNCTION__);
  937         $response = $client->request('GET', 'http://localhost:8057/max-duration', [
  938             'max_duration' => 0.1,
  939         ]);
  940 
  941         $start = microtime(true);
  942 
  943         try {
  944             $response->getContent();
  945         } catch (TransportExceptionInterface $e) {
  946             $this->addToAssertionCount(1);
  947         }
  948 
  949         $duration = microtime(true) - $start;
  950 
  951         $this->assertLessThan(10, $duration);
  952     }
  953 }