PluginProbe
WPFunnels – Funnel Builder for WooCommerce with Checkout & One Click Upsell / 2.8.16
WPFunnels – Funnel Builder for WooCommerce with Checkout & One Click Upsell v2.8.16
3.13.3 3.13.2 3.13.1 3.13.0 3.12.13 3.12.12 3.12.11 3.12.10 3.12.9 3.12.8 3.12.7 3.12.6 3.12.5 3.12.4 3.12.3 3.12.1 3.12.2 3.12.0 3.11.1 3.11.0 3.10.9 3.10.8 3.10.7 3.10.6 2.8.16 All 261 releases
wpfunnels / vendor / symfony / http-client-contracts / Test / HttpClientTestCase.php

HttpClientTestCase.php in WPFunnels – Funnel Builder for WooCommerce with Checkout & One Click Upsell 2.8.16, at vendor/symfony/http-client-contracts/Test/HttpClientTestCase.php

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