scratch – Blame information for rev

Subversion Repositories:
Rev:
Rev Author Line No. Line
87 office 1 <?php
2  
3 namespace GuzzleHttp\Tests\Adapter\Curl;
4  
5 require_once __DIR__ . '/AbstractCurl.php';
6  
7 use GuzzleHttp\Adapter\Curl\MultiAdapter;
8 use GuzzleHttp\Adapter\Transaction;
9 use GuzzleHttp\Client;
10 use GuzzleHttp\Event\CompleteEvent;
11 use GuzzleHttp\Event\ErrorEvent;
12 use GuzzleHttp\Exception\RequestException;
13 use GuzzleHttp\Message\MessageFactory;
14 use GuzzleHttp\Message\Request;
15 use GuzzleHttp\Message\Response;
16 use GuzzleHttp\Stream\NoSeekStream;
17 use GuzzleHttp\Stream\Stream;
18 use GuzzleHttp\Tests\Server;
19  
20 /**
21 * @covers GuzzleHttp\Adapter\Curl\MultiAdapter
22 */
23 class MultiAdapterTest extends AbstractCurl
24 {
25 protected function getAdapter($factory = null, $options = [])
26 {
27 return new MultiAdapter($factory ?: new MessageFactory(), $options);
28 }
29  
30 public function testSendsSingleRequest()
31 {
32 Server::flush();
33 Server::enqueue("HTTP/1.1 200 OK\r\nFoo: bar\r\nContent-Length: 0\r\n\r\n");
34 $t = new Transaction(new Client(), new Request('GET', Server::$url));
35 $a = new MultiAdapter(new MessageFactory());
36 $response = $a->send($t);
37 $this->assertEquals(200, $response->getStatusCode());
38 $this->assertEquals('bar', $response->getHeader('Foo'));
39 }
40  
41 public function testCanSetSelectTimeout()
42 {
43 $current = isset($_SERVER[MultiAdapter::ENV_SELECT_TIMEOUT])
44 ? $_SERVER[MultiAdapter::ENV_SELECT_TIMEOUT]: null;
45 unset($_SERVER[MultiAdapter::ENV_SELECT_TIMEOUT]);
46 $a = new MultiAdapter(new MessageFactory());
47 $this->assertEquals(1, $this->readAttribute($a, 'selectTimeout'));
48 $a = new MultiAdapter(new MessageFactory(), ['select_timeout' => 10]);
49 $this->assertEquals(10, $this->readAttribute($a, 'selectTimeout'));
50 $_SERVER[MultiAdapter::ENV_SELECT_TIMEOUT] = 2;
51 $a = new MultiAdapter(new MessageFactory());
52 $this->assertEquals(2, $this->readAttribute($a, 'selectTimeout'));
53 $_SERVER[MultiAdapter::ENV_SELECT_TIMEOUT] = $current;
54 }
55  
56 public function testCanSetMaxHandles()
57 {
58 $a = new MultiAdapter(new MessageFactory());
59 $this->assertEquals(3, $this->readAttribute($a, 'maxHandles'));
60 $a = new MultiAdapter(new MessageFactory(), ['max_handles' => 10]);
61 $this->assertEquals(10, $this->readAttribute($a, 'maxHandles'));
62 }
63  
64 /**
65 * @expectedException \GuzzleHttp\Exception\AdapterException
66 * @expectedExceptionMessage cURL error -2:
67 */
68 public function testChecksCurlMultiResult()
69 {
70 MultiAdapter::throwMultiError(-2);
71 }
72  
73 public function testChecksForCurlException()
74 {
75 $mh = curl_multi_init();
76 $request = new Request('GET', 'http://httbin.org');
77 $transaction = $this->getMockBuilder('GuzzleHttp\Adapter\Transaction')
78 ->setMethods(['getRequest'])
79 ->disableOriginalConstructor()
80 ->getMock();
81 $transaction->expects($this->exactly(2))
82 ->method('getRequest')
83 ->will($this->returnValue($request));
84 $context = $this->getMockBuilder('GuzzleHttp\Adapter\Curl\BatchContext')
85 ->setMethods(['throwsExceptions'])
86 ->setConstructorArgs([$mh, true])
87 ->getMock();
88 $context->expects($this->once())
89 ->method('throwsExceptions')
90 ->will($this->returnValue(true));
91 $a = new MultiAdapter(new MessageFactory());
92 $r = new \ReflectionMethod($a, 'isCurlException');
93 $r->setAccessible(true);
94 try {
95 $r->invoke($a, $transaction, ['result' => -10], $context, []);
96 curl_multi_close($mh);
97 $this->fail('Did not throw');
98 } catch (RequestException $e) {
99 curl_multi_close($mh);
100 $this->assertSame($request, $e->getRequest());
101 $this->assertContains('[curl] (#-10) ', $e->getMessage());
102 $this->assertContains($request->getUrl(), $e->getMessage());
103 }
104 }
105  
106 public function testSendsParallelRequestsFromQueue()
107 {
108 $c = new Client();
109 Server::flush();
110 Server::enqueue([
111 "HTTP/1.1 200 OK\r\nContent-Length: 0\r\n\r\n",
112 "HTTP/1.1 200 OK\r\nContent-Length: 0\r\n\r\n",
113 "HTTP/1.1 200 OK\r\nContent-Length: 0\r\n\r\n",
114 "HTTP/1.1 200 OK\r\nContent-Length: 0\r\n\r\n"
115 ]);
116 $transactions = [
117 new Transaction($c, new Request('GET', Server::$url)),
118 new Transaction($c, new Request('PUT', Server::$url)),
119 new Transaction($c, new Request('HEAD', Server::$url)),
120 new Transaction($c, new Request('GET', Server::$url))
121 ];
122 $a = new MultiAdapter(new MessageFactory());
123 $a->sendAll(new \ArrayIterator($transactions), 2);
124 foreach ($transactions as $t) {
125 $response = $t->getResponse();
126 $this->assertNotNull($response);
127 $this->assertEquals(200, $response->getStatusCode());
128 }
129 }
130  
131 public function testCreatesAndReleasesHandlesWhenNeeded()
132 {
133 $a = new MultiAdapter(new MessageFactory());
134 $c = new Client([
135 'adapter' => $a,
136 'base_url' => Server::$url
137 ]);
138  
139 Server::flush();
140 Server::enqueue([
141 "HTTP/1.1 200 OK\r\nContent-Length: 0\r\n\r\n",
142 "HTTP/1.1 200 OK\r\nContent-Length: 0\r\n\r\n",
143 "HTTP/1.1 200 OK\r\nContent-Length: 0\r\n\r\n",
144 "HTTP/1.1 200 OK\r\nContent-Length: 0\r\n\r\n",
145 "HTTP/1.1 200 OK\r\nContent-Length: 0\r\n\r\n",
146 "HTTP/1.1 200 OK\r\nContent-Length: 0\r\n\r\n",
147 "HTTP/1.1 200 OK\r\nContent-Length: 0\r\n\r\n",
148 ]);
149  
150 $ef = function (ErrorEvent $e) { throw $e->getException(); };
151  
152 $request1 = $c->createRequest('GET', '/');
153 $request1->getEmitter()->on('headers', function () use ($a, $c, $ef) {
154 $a->send(new Transaction($c, $c->createRequest('GET', '/', [
155 'events' => [
156 'headers' => function () use ($a, $c, $ef) {
157 $r = $c->createRequest('GET', '/', [
158 'events' => ['error' => ['fn' => $ef, 'priority' => 9999]]
159 ]);
160 $r->getEmitter()->once('headers', function () use ($a, $c, $r) {
161 $a->send(new Transaction($c, $r));
162 });
163 $a->send(new Transaction($c, $r));
164 // Now, reuse an existing handle
165 $a->send(new Transaction($c, $r));
166 },
167 'error' => ['fn' => $ef, 'priority' => 9999]
168 ]
169 ])));
170 });
171  
172 $request1->getEmitter()->on('error', $ef);
173  
174 $transactions = [
175 new Transaction($c, $request1),
176 new Transaction($c, $c->createRequest('PUT')),
177 new Transaction($c, $c->createRequest('HEAD'))
178 ];
179  
180 $a->sendAll(new \ArrayIterator($transactions), 2);
181  
182 foreach ($transactions as $index => $t) {
183 $response = $t->getResponse();
184 $this->assertInstanceOf(
185 'GuzzleHttp\\Message\\ResponseInterface',
186 $response,
187 'Transaction at index ' . $index . ' did not populate response'
188 );
189 $this->assertEquals(200, $response->getStatusCode());
190 }
191 }
192  
193 public function testThrowsAndReleasesWhenErrorDuringCompleteEvent()
194 {
195 Server::flush();
196 Server::enqueue("HTTP/1.1 500 Internal Server Error\r\nContent-Length: 0\r\n\r\n");
197 $request = new Request('GET', Server::$url);
198 $request->getEmitter()->on('complete', function (CompleteEvent $e) {
199 throw new RequestException('foo', $e->getRequest());
200 });
201 $t = new Transaction(new Client(), $request);
202 $a = new MultiAdapter(new MessageFactory());
203 try {
204 $a->send($t);
205 $this->fail('Did not throw');
206 } catch (RequestException $e) {
207 $this->assertSame($request, $e->getRequest());
208 }
209 }
210  
211 public function testEnsuresResponseWasSetForGet()
212 {
213 $client = new Client();
214 $request = $client->createRequest('GET', Server::$url);
215 $response = new Response(200, []);
216 $er = null;
217  
218 $request->getEmitter()->on(
219 'error',
220 function (ErrorEvent $e) use (&$er, $response) {
221 $er = $e;
222 }
223 );
224  
225 $transaction = $this->getMockBuilder('GuzzleHttp\Adapter\Transaction')
226 ->setMethods(['getResponse', 'setResponse'])
227 ->setConstructorArgs([$client, $request])
228 ->getMock();
229 $transaction->expects($this->any())->method('setResponse');
230 $transaction->expects($this->any())
231 ->method('getResponse')
232 ->will($this->returnCallback(function () use ($response) {
233 $caller = debug_backtrace()[6]['function'];
234 return $caller == 'addHandle' ||
235 $caller == 'validateResponseWasSet'
236 ? null
237 : $response;
238 }));
239  
240 $a = new MultiAdapter(new MessageFactory());
241 Server::flush();
242 Server::enqueue(["HTTP/1.1 200 OK\r\nContent-Length: 0\r\n\r\n"]);
243 $a->sendAll(new \ArrayIterator([$transaction]), 10);
244 $this->assertNotNull($er);
245  
246 $this->assertContains(
247 'No response was received',
248 $er->getException()->getMessage()
249 );
250 }
251  
252 private function runConnectionTest(
253 $queue,
254 $stream,
255 $msg,
256 $statusCode = null
257 ) {
258 $obj = new \stdClass();
259 $er = null;
260 $client = new Client();
261 $request = $client->createRequest('PUT', Server::$url, [
262 'body' => $stream
263 ]);
264  
265 $request->getEmitter()->on(
266 'error',
267 function (ErrorEvent $e) use (&$er) {
268 $er = $e;
269 }
270 );
271  
272 $transaction = $this->getMockBuilder('GuzzleHttp\Adapter\Transaction')
273 ->setMethods(['getResponse', 'setResponse'])
274 ->setConstructorArgs([$client, $request])
275 ->getMock();
276  
277 $transaction->expects($this->any())
278 ->method('setResponse')
279 ->will($this->returnCallback(function ($r) use (&$obj) {
280 $obj->res = $r;
281 }));
282  
283 $transaction->expects($this->any())
284 ->method('getResponse')
285 ->will($this->returnCallback(function () use ($obj, &$called) {
286 $caller = debug_backtrace()[6]['function'];
287 if ($caller == 'addHandle') {
288 return null;
289 } elseif ($caller == 'validateResponseWasSet') {
290 return ++$called == 2 ? $obj->res : null;
291 } else {
292 return $obj->res;
293 }
294 }));
295  
296 $a = new MultiAdapter(new MessageFactory());
297 Server::flush();
298 Server::enqueue($queue);
299 $a->sendAll(new \ArrayIterator([$transaction]), 10);
300  
301 if ($msg) {
302 $this->assertNotNull($er);
303 $this->assertContains($msg, $er->getException()->getMessage());
304 } else {
305 $this->assertEquals(
306 $statusCode,
307 $transaction->getResponse()->getStatusCode()
308 );
309 }
310 }
311  
312 public function testThrowsWhenTheBodyCannotBeRewound()
313 {
314 $this->runConnectionTest(
315 ["HTTP/1.1 200 OK\r\nContent-Length: 0\r\n\r\n"],
316 new NoSeekStream(Stream::factory('foo')),
317 'attempting to rewind the request body failed'
318 );
319 }
320  
321 public function testRetriesRewindableStreamsWhenClosedConnectionErrors()
322 {
323 $this->runConnectionTest(
324 [
325 "HTTP/1.1 200 OK\r\nContent-Length: 0\r\n\r\n",
326 "HTTP/1.1 201 OK\r\nContent-Length: 0\r\n\r\n",
327 ],
328 Stream::factory('foo'),
329 false,
330 201
331 );
332 }
333  
334 public function testThrowsImmediatelyWhenInstructed()
335 {
336 Server::flush();
337 Server::enqueue(["HTTP/1.1 501\r\nContent-Length: 0\r\n\r\n"]);
338 $c = new Client(['base_url' => Server::$url]);
339 $request = $c->createRequest('GET', '/');
340 $request->getEmitter()->on('error', function (ErrorEvent $e) {
341 $e->throwImmediately(true);
342 });
343 $transactions = [new Transaction($c, $request)];
344 $a = new MultiAdapter(new MessageFactory());
345 try {
346 $a->sendAll(new \ArrayIterator($transactions), 1);
347 $this->fail('Did not throw');
348 } catch (RequestException $e) {
349 $this->assertSame($request, $e->getRequest());
350 }
351 }
352  
353 public function testRewindsStreamOnComplete()
354 {
355 Server::flush();
356 Server::enqueue("HTTP/1.1 200 OK\r\nFoo: bar\r\nContent-Length: 4\r\n\r\ntest");
357 $t = new Transaction(new Client(), new Request('GET', Server::$url));
358 $a = new MultiAdapter(new MessageFactory());
359 $response = $a->send($t);
360 $this->assertEquals('test', $response->getBody()->read(4));
361 }
362 }