38 CURLM_BAD_HANDLE => array(
'CURLM_BAD_HANDLE',
'The passed-in handle is not a valid CURLM handle.'),
39 CURLM_BAD_EASY_HANDLE => array(
'CURLM_BAD_EASY_HANDLE',
"An easy handle was not good/valid. It could mean that it isn't an easy handle at all, or possibly that the handle already is in used by this or another multi handle."),
40 CURLM_OUT_OF_MEMORY => array(
'CURLM_OUT_OF_MEMORY',
'You are doomed.'),
41 CURLM_INTERNAL_ERROR => array(
'CURLM_INTERNAL_ERROR',
'This can only be returned if libcurl bugs. Please report it to us!')
50 $this->multiHandle = curl_multi_init();
52 if ($this->multiHandle ===
false) {
61 if (is_resource($this->multiHandle)) {
62 curl_multi_close($this->multiHandle);
68 $this->requests[] = $request;
72 $this->
dispatch(self::ADD_REQUEST, array(
'request' => $request));
85 if (($index = array_search($request, $this->requests,
true)) !==
false) {
86 $request = $this->requests[$index];
87 unset($this->requests[$index]);
88 $this->requests = array_values($this->requests);
89 $this->
dispatch(self::REMOVE_REQUEST, array(
'request' => $request));
96 public function reset($hard =
false)
99 if ($this->requests) {
100 foreach ($this->requests as $request) {
101 $this->
remove($request);
105 $this->handles = new \SplObjectStorage();
106 $this->requests = $this->resourceHash = $this->exceptions = $this->successful = array();
109 public function send()
121 public function count()
123 return count($this->requests);
138 $multiException->addFailedRequestWithException($e[
'request'], $e[
'exception']);
143 if (!$multiException->containsRequest($request)) {
144 $multiException->addSuccessfulRequest($request);
148 throw $multiException;
162 $this->addHandle($request);
166 $this->
remove($request);
168 $this->successful[] = $request;
171 }
catch (\Exception $e) {
180 $this->checkCurlResult(
181 curl_multi_add_handle($this->multiHandle, $handle)
195 $this->handles[$request] = $wrapper;
196 $this->resourceHash[(int) $wrapper->getHandle()] = $request;
206 $event =
new Event(array(
'curl_multi' => $this));
208 while ($this->requests) {
210 $blocking = $total = 0;
211 foreach ($this->requests as $request) {
213 $event[
'request'] = $request;
214 $request->getEventDispatcher()->dispatch(self::POLLING_REQUEST, $event);
216 if ($request->getParams()->hasKey(self::BLOCKING)) {
220 if ($blocking == $total) {
224 $this->executeHandles();
232 private function executeHandles()
238 while (($mrc = curl_multi_exec($this->multiHandle, $active)) == CURLM_CALL_MULTI_PERFORM);
239 $this->checkCurlResult($mrc);
240 $this->processMessages();
241 if ($active && curl_multi_select($this->multiHandle,
$selectTimeout) === -1) {
252 private function processMessages()
254 while ($done = curl_multi_info_read($this->multiHandle)) {
255 $request = $this->resourceHash[(int) $done[
'handle']];
258 $this->successful[] = $request;
259 }
catch (\Exception $e) {
273 $this->exceptions[] = array(
'request' => $request,
'exception' => $e);
274 $this->
remove($request);
275 $this->
dispatch(self::MULTI_EXCEPTION, array(
'exception' => $e,
'all_exceptions' => $this->exceptions));
287 protected function processResponse(RequestInterface $request, CurlHandle $handle, array $curl)
290 $handle->updateRequestFromTransfer($request);
292 $curlException = $this->isCurlException($request, $handle, $curl);
298 if (!$curlException) {
299 if ($this->validateResponseWasSet($request)) {
300 $state = $request->setState(
302 array(
'handle' => $handle)
307 $this->
remove($request);
317 $this->
remove($request);
323 throw $curlException;
332 protected function removeHandle(RequestInterface $request)
334 if (isset($this->handles[$request])) {
335 $handle = $this->handles[$request];
336 curl_multi_remove_handle($this->multiHandle, $handle->getHandle());
337 unset($this->handles[$request]);
338 unset($this->resourceHash[(
int) $handle->getHandle()]);
352 private function isCurlException(RequestInterface $request, CurlHandle $handle, array $curl)
354 if (CURLM_OK == $curl[
'result'] || CURLM_CALL_MULTI_PERFORM == $curl[
'result']) {
358 $handle->setErrorNo($curl[
'result']);
360 $handle->getErrorNo(), $handle->getError(), $handle->getUrl()));
361 $e->setCurlHandle($handle)
362 ->setRequest($request)
363 ->setCurlInfo($handle->getInfo())
364 ->setError($handle->getError(), $handle->getErrorNo());
375 private function checkCurlResult($code)
377 if ($code != CURLM_OK && $code != CURLM_CALL_MULTI_PERFORM) {
378 throw new CurlException(isset($this->multiErrors[$code])
379 ?
"cURL error: {$code} ({$this->multiErrors[$code][0]}): cURL message: {$this->multiErrors[$code][1]}"
380 :
'Unexpected cURL error: ' . $code
388 private function validateResponseWasSet(RequestInterface $request)
390 if ($request->getResponse()) {
394 $body = $request instanceof EntityEnclosingRequestInterface
395 ? $request->getBody()
399 $rex =
new RequestException(
400 'No response was received for a request with no body. This'
401 .
' could mean that you are saturating your network.'
403 $rex->setRequest($request);
405 } elseif (!$body->isSeekable() || !$body->seek(0)) {
407 $rex =
new RequestException(
408 'The connection was unexpectedly closed. The request would'
409 .
' have been retried, but attempting to rewind the'
410 .
' request body failed.'
412 $rex->setRequest($request);
415 $this->
remove($request);
417 $this->requests[] = $request;
418 $this->addHandle($request);