1<?php
2namespace GuzzleHttp\Tests\Ring\Client;
3
4use GuzzleHttp\Ring\Client\CurlMultiHandler;
5
6class CurlMultiHandlerTest extends \PHPUnit_Framework_TestCase
7{
8    public function testSendsRequest()
9    {
10        Server::enqueue([['status' => 200]]);
11        $a = new CurlMultiHandler();
12        $response = $a([
13            'http_method' => 'GET',
14            'headers'     => ['host' => [Server::$host]],
15        ]);
16        $this->assertInstanceOf('GuzzleHttp\Ring\Future\FutureArray', $response);
17        $this->assertEquals(200, $response['status']);
18        $this->assertArrayHasKey('transfer_stats', $response);
19        $realUrl = trim($response['transfer_stats']['url'], '/');
20        $this->assertEquals(trim(Server::$url, '/'), $realUrl);
21        $this->assertArrayHasKey('effective_url', $response);
22        $this->assertEquals(
23            trim(Server::$url, '/'),
24            trim($response['effective_url'], '/')
25        );
26    }
27
28    public function testCreatesErrorResponses()
29    {
30        $url = 'http://localhost:123/';
31        $a = new CurlMultiHandler();
32        $response = $a([
33            'http_method' => 'GET',
34            'headers'     => ['host' => ['localhost:123']],
35        ]);
36        $this->assertInstanceOf('GuzzleHttp\Ring\Future\FutureArray', $response);
37        $this->assertNull($response['status']);
38        $this->assertNull($response['reason']);
39        $this->assertEquals([], $response['headers']);
40        $this->assertArrayHasKey('error', $response);
41        $this->assertContains('cURL error ', $response['error']->getMessage());
42        $this->assertArrayHasKey('transfer_stats', $response);
43        $this->assertEquals(
44            trim($url, '/'),
45            trim($response['transfer_stats']['url'], '/')
46        );
47        $this->assertArrayHasKey('effective_url', $response);
48        $this->assertEquals(
49            trim($url, '/'),
50            trim($response['effective_url'], '/')
51        );
52    }
53
54    public function testSendsFuturesWhenDestructed()
55    {
56        Server::enqueue([['status' => 200]]);
57        $a = new CurlMultiHandler();
58        $response = $a([
59            'http_method' => 'GET',
60            'headers'     => ['host' => [Server::$host]],
61        ]);
62        $this->assertInstanceOf('GuzzleHttp\Ring\Future\FutureArray', $response);
63        $a->__destruct();
64        $this->assertEquals(200, $response['status']);
65    }
66
67    public function testCanSetMaxHandles()
68    {
69        $a = new CurlMultiHandler(['max_handles' => 2]);
70        $this->assertEquals(2, $this->readAttribute($a, 'maxHandles'));
71    }
72
73    public function testCanSetSelectTimeout()
74    {
75        $a = new CurlMultiHandler(['select_timeout' => 2]);
76        $this->assertEquals(2, $this->readAttribute($a, 'selectTimeout'));
77    }
78
79    public function testSendsFuturesWhenMaxHandlesIsReached()
80    {
81        $request = [
82            'http_method' => 'PUT',
83            'headers'     => ['host' => [Server::$host]],
84            'future'      => 'lazy', // passing this to control the test
85        ];
86        $response = ['status' => 200];
87        Server::flush();
88        Server::enqueue([$response, $response, $response]);
89        $a = new CurlMultiHandler(['max_handles' => 3]);
90        for ($i = 0; $i < 5; $i++) {
91            $responses[] = $a($request);
92        }
93        $this->assertCount(3, Server::received());
94        $responses[3]->cancel();
95        $responses[4]->cancel();
96    }
97
98    public function testCanCancel()
99    {
100        Server::flush();
101        $response = ['status' => 200];
102        Server::enqueue(array_fill_keys(range(0, 10), $response));
103        $a = new CurlMultiHandler();
104        $responses = [];
105
106        for ($i = 0; $i < 10; $i++) {
107            $response = $a([
108                'http_method' => 'GET',
109                'headers'     => ['host' => [Server::$host]],
110                'future'      => 'lazy',
111            ]);
112            $response->cancel();
113            $responses[] = $response;
114        }
115
116        $this->assertCount(0, Server::received());
117
118        foreach ($responses as $response) {
119            $this->assertTrue($this->readAttribute($response, 'isRealized'));
120        }
121    }
122
123    public function testCannotCancelFinished()
124    {
125        Server::flush();
126        Server::enqueue([['status' => 200]]);
127        $a = new CurlMultiHandler();
128        $response = $a([
129            'http_method' => 'GET',
130            'headers'     => ['host' => [Server::$host]],
131        ]);
132        $response->wait();
133        $response->cancel();
134    }
135
136    public function testDelaysInParallel()
137    {
138        Server::flush();
139        Server::enqueue([['status' => 200]]);
140        $a = new CurlMultiHandler();
141        $expected = microtime(true) + (100 / 1000);
142        $response = $a([
143            'http_method' => 'GET',
144            'headers'     => ['host' => [Server::$host]],
145            'client'      => ['delay' => 100],
146        ]);
147        $response->wait();
148        $this->assertGreaterThanOrEqual($expected, microtime(true));
149    }
150
151    public function testSendsNonLazyFutures()
152    {
153        $request = [
154            'http_method' => 'GET',
155            'headers'     => ['host' => [Server::$host]],
156            'future'      => true,
157        ];
158        Server::flush();
159        Server::enqueue([['status' => 202]]);
160        $a = new CurlMultiHandler();
161        $response = $a($request);
162        $this->assertInstanceOf('GuzzleHttp\Ring\Future\FutureArray', $response);
163        $this->assertEquals(202, $response['status']);
164    }
165
166    public function testExtractsErrors()
167    {
168        $request = [
169            'http_method' => 'GET',
170            'headers'     => ['host' => ['127.0.0.1:123']],
171            'future'      => true,
172        ];
173        Server::flush();
174        Server::enqueue([['status' => 202]]);
175        $a = new CurlMultiHandler();
176        $response = $a($request);
177        $this->assertInstanceOf('GuzzleHttp\Ring\Future\FutureArray', $response);
178        $this->assertEquals(CURLE_COULDNT_CONNECT, $response['curl']['errno']);
179        $this->assertNotEmpty($response['curl']['error']);
180    }
181}
182