/** * Execute multi curl of this Streamer * * @return boolean * @throws Exception */ public function exec() { if (!$this->isResource()) { throw new Exception("Is not a valid cURL Multi Handle resource", Exception::INVALID_MULTI_CURL); } if (empty($this->streams)) { throw new Exception("Pull of streams is empty", Exception::PULL_IS_EMPTY); } $running = $messages = 0; do { // executing... if (($error = curl_multi_exec($this->curl, $running)) != 0) { throw new Exception(curl_multi_strerror($error), Exception::MULTI_CURL_ERROR); } // we have some completed streams in this iteration do { if ($read = curl_multi_info_read($this->curl, $messages)) { $handle = $read['handle']; /** @var $stream Stream */ $stream = $this->streams[(int) $handle]; $stream->setResponse($read['result'], curl_multi_getcontent($handle)); } } while ($messages); // in god we trust... usleep(1000); } while ($running); // close descriptors $this->closeResource(); return $this; }
public function get($url_mixed, $data = array()) { if (is_array($url_mixed)) { $curl_multi = curl_multi_init(); $this->_multi_parent = true; $this->curls = array(); foreach ($url_mixed as $url) { $curl = new Curl(); $curl->_multi_child = true; $curl->setOpt(CURLOPT_URL, $this->_buildURL($url, $data), $curl->curl); $curl->setOpt(CURLOPT_HTTPGET, true); $this->_call($this->_before_send, $curl); $this->curls[] = $curl; $curlm_error_code = curl_multi_add_handle($curl_multi, $curl->curl); if (!($curlm_error_code === CURLM_OK)) { throw new \ErrorException('cURL multi add handle error: ' . curl_multi_strerror($curlm_error_code)); } } foreach ($this->curls as $ch) { foreach ($this->_options as $key => $value) { $ch->setOpt($key, $value); } } do { $status = curl_multi_exec($curl_multi, $active); } while ($status === CURLM_CALL_MULTI_PERFORM || $active); foreach ($this->curls as $ch) { $this->exec($ch); } } else { $this->setopt(CURLOPT_URL, $this->_buildURL($url_mixed, $data)); $this->setopt(CURLOPT_HTTPGET, true); return $this->exec(); } }
public function execute() { $mh = curl_multi_init(); $chs = $this->chs(); $stillRunning = 0; $result = array(); if (function_exists('curl_multi_setopt')) { curl_multi_setopt($mh, CURLMOPT_PIPELINING, 1); curl_multi_setopt($mh, CURLMOPT_MAXCONNECTS, $this->maxConnectsSize()); } foreach ($chs as $ch) { curl_multi_add_handle($mh, $ch); } do { $execReturnValue = curl_multi_exec($mh, $stillRunning); curl_multi_select($mh); } while ($stillRunning > 0); foreach ($chs as $i => $ch) { $curlError = curl_error($ch); if ($curlError === "") { $result[$i] = curl_multi_getcontent($ch); } else { throw new PubnubException("Curl error on handle {$i}: {$curlError}\n"); } curl_multi_remove_handle($mh, $ch); curl_close($ch); } curl_multi_close($mh); $this->requests = array(); if ($execReturnValue != CURLM_OK) { throw new PubnubException(curl_multi_strerror($execReturnValue)); } return $result; }
public function get($url_mixed, $data = array()) { if (is_array($url_mixed)) { $curl_multi = curl_multi_init(); $this->multi_parent = true; $this->curls = array(); foreach ($url_mixed as $url) { $curl = new Curl(); $curl->multi_child = true; $curl->beforeSend($this->before_send_function); $curl->success($this->success_function); $curl->error($this->error_function); $curl->complete($this->complete_function); $curl->base_url = $url; $curl->url = $this->buildURL($url, $data); $curl->setOpt(CURLOPT_URL, $curl->url, $curl->curl); $curl->setOpt(CURLOPT_CUSTOMREQUEST, 'GET'); $curl->setOpt(CURLOPT_HTTPGET, true); $this->curls[] = $curl; $curlm_error_code = curl_multi_add_handle($curl_multi, $curl->curl); if (!($curlm_error_code === CURLM_OK)) { throw new \ErrorException('cURL multi add handle error: ' . curl_multi_strerror($curlm_error_code)); } } foreach ($this->curls as $ch) { foreach ($this->options as $key => $value) { $ch->setOpt($key, $value); } } do { curl_multi_select($curl_multi); $status = curl_multi_exec($curl_multi, $active); } while ($status === CURLM_CALL_MULTI_PERFORM || $active); while (!($info_array = curl_multi_info_read($curl_multi)) === false) { if (!($info_array['msg'] === CURLMSG_DONE)) { continue; } foreach ($this->curls as $ch) { if ($ch->curl === $info_array['handle']) { $ch->curl_error_code = $info_array['result']; break; } } } foreach ($this->curls as $ch) { $this->exec($ch); } } else { $this->base_url = $url_mixed; $this->url = $this->buildURL($url_mixed, $data); $this->setOpt(CURLOPT_URL, $this->url); $this->setOpt(CURLOPT_CUSTOMREQUEST, 'GET'); $this->setOpt(CURLOPT_HTTPGET, true); return $this->exec(); } }
/** * Call curl_multi_add_handle(). * @param resource $ch * @param Deferred $deferred */ private function addImmediate($ch, Deferred $deferred = null) { $errno = curl_multi_add_handle($this->mh, $ch); if ($errno !== CURLM_OK) { // @codeCoverageIgnoreStart $msg = curl_multi_strerror($errno) . ": {$ch}"; $deferred && $deferred->reject(new \RuntimeException($msg)); return; // @codeCoverageIgnoreEnd } $this->added[(string) $ch] = $ch; $deferred && ($this->deferreds[(string) $ch] = $deferred); }
/** * Call curl_multi_add_handle(). * @param resource $ch * @return PromiseInterface */ public function add($ch) { $deferred = new Deferred(); $errno = curl_multi_add_handle($this->mh, $ch); if ($errno !== CURLM_OK) { // @codeCoverageIgnoreStart $msg = curl_multi_strerror($errno) . ": {$ch}"; $deferred->reject(new \RuntimeException($msg)); return $deferred->promise(); // @codeCoverageIgnoreEnd } $this->added[(string) $ch] = $ch; $this->deferreds[(string) $ch] = $deferred; return $deferred->promise(); }
<?php var_dump(strtolower(curl_multi_strerror(CURLM_OK))); var_dump(strtolower(curl_multi_strerror(CURLM_BAD_HANDLE)));
/** * Add Handle * * @access private * @param $curl */ private function addHandle($curl) { $curlm_error_code = curl_multi_add_handle($this->multi_curl, $curl->curl); if (!($curlm_error_code === CURLM_OK)) { throw new \ErrorException('cURL multi add handle error: ' . curl_multi_strerror($curlm_error_code)); } $curl->beforeSend($this->before_send_function); $curl->success($this->success_function); $curl->error($this->error_function); $curl->complete($this->complete_function); $this->curls[] = $curl; $curl->id = count($this->curls); }
/** * Throw an exception for a cURL multi response * * @param int $code Curl response code * @throws AdapterException */ public static function throwMultiError($code) { $buffer = function_exists('curl_multi_strerror') ? curl_multi_strerror($code) : self::ERROR_STR; throw new AdapterException(sprintf('cURL error %s: %s', $code, $buffer)); }
/** * @see curl_multi_strerror * * @param int $errornum * @return string */ public static function strerror($errornum) { return curl_multi_strerror($errornum); }
/** * @note PHP 5.5.0 * * @since 1.0 * * @return string */ public function getLastError() { return function_exists('curl_multi_strerror') ? curl_multi_strerror($this->lastErrorCode) : ''; }
/** * Return string describing error code * * @param int $errornum One of the CURLM error codes constants. * @return string Error string for valid error code, NULL otherwise. */ public final function curlMultiStrerror($errornum) { return curl_multi_strerror($this->curl_multi_handle, $errornum); }
public function getError($errno) { return curl_multi_strerror($errno); }
/** * 发送异步请求 * @return void */ public function sendRequestAsyn() { $mh = curl_multi_init(); $ch_handles = array(); // 遍历设置的连接 foreach ($this->asynRequests as $request) { $ch = curl_init($request['url']); $this->setCurlOptions($ch, $request['options']); $ch_handles[] = $ch; // 添加$ch到$mh curl_multi_add_handle($mh, $ch); } $running = null; do { $mrc = curl_multi_exec($mh, $running); curl_multi_select($mh); } while ($running > 0); unset($running); if ($mrc !== CURLM_OK) { throw new \Exception(curl_multi_strerror($mrc), 1); } foreach ($ch_handles as $key => $ch) { $this->headerInfo = curl_getinfo($ch); $this->asynResponses[$key] = new \stdClass(); $this->asynResponses[$key]->response = curl_multi_getcontent($ch); $this->asynResponses[$key]->responseBody = $this->parseResultBody($this->asynResponses[$key]->response); $this->asynResponses[$key]->responseHeaders = $this->parseResultHeaders($this->asynResponses[$key]->response); curl_multi_remove_handle($mh, $ch); } curl_multi_close($mh); }
/** * @param resource $asyncHandle * @param array &$asyncPendingRequests * @param array &$asyncProcessingRequests * @param array $asyncOptions * @return bool */ private static function fetchAsyncRequests($asyncHandle, &$asyncPendingRequests, &$asyncProcessingRequests, $asyncOptions) { $pendingRequestCount = count($asyncPendingRequests); $processingRequestCount = count($asyncProcessingRequests); $maxConcurrentRequests = self::getAsyncMaxConcurrentRequests($asyncOptions); $requestFetchingCallback = self::getAsyncRequestFetchingCallback($asyncOptions); $isFetchingCallbackInvoked = false; while ($processingRequestCount < $maxConcurrentRequests) { if ($pendingRequestCount === 0) { if ($isFetchingCallbackInvoked) { return true; } if ($requestFetchingCallback !== null) { $tmp = call_user_func($requestFetchingCallback); if ($tmp === false) { return false; } elseif ($tmp === true || $tmp === null) { return true; } elseif (is_array($tmp) === false) { $type = gettype($tmp); throw new WebClientAsyncException("The return value of request fetching callback must" . " be an array or a bool, {$type} given."); } $pendingRequestCount = count($tmp); if ($pendingRequestCount === 0) { return true; } $asyncPendingRequests = $tmp; $isFetchingCallbackInvoked = true; } else { return false; } } $key = key($asyncPendingRequests); $client = $asyncPendingRequests[$key]; unset($asyncPendingRequests[$key]); --$pendingRequestCount; ++$processingRequestCount; if (is_array($client)) { $tmp = new static(); $tmp->setOptions($client); $client = $tmp; } elseif ($client instanceof WebClient === false) { $type = gettype($client); throw new WebClientAsyncException('The request must be an instance of ' . __CLASS__ . " or an option array, {$type} given."); } $client->requestOptions = $client->options; $client->initializeRequest(); $index = (int) $client->handle; if (isset($asyncProcessingRequests[$index])) { throw new WebClientAsyncException('The web client already exists in the processing queue.'); } $asyncProcessingRequests[$index] = $client; $code = curl_multi_add_handle($asyncHandle, $client->handle); if ($code !== CURLM_OK) { throw new WebClientAsyncException(curl_multi_strerror($code), $code); } } return true; }
// /301?to=<url> returns a 301 to a new URL // /304 returns an HTTP 304 $curl_handles = array(); $master = curl_multi_init(); $urls = ['http://localhost:3001/200', 'http://localhost:3001/200?wait=5', 'http://localhost:3001/301', 'http://localhost:3001/302', 'http://localhost:3001/304', 'http://localhost:3001/404', 'http://localhost:3002/blah', 'http://localhost:3001/slowloris', 'http://localhost:3001/500']; for ($i = 0; $i < count($urls); $i++) { $handle = curl_init($urls[$i]); curl_setopt($handle, CURLOPT_RETURNTRANSFER, true); curl_setopt($handle, CURLOPT_FOLLOWLOCATION, true); curl_multi_add_handle($master, $handle); $curl_handles[$i] = $handle; } echo "starting\n"; do { $mrc = curl_multi_exec($master, $running); echo "new state " . curl_multi_strerror($mrc) . "\n"; echo "startup\n"; } while ($mrc == CURLM_CALL_MULTI_PERFORM); echo "running\n"; while ($running && $mrc == CURLM_OK) { //echo "selecting\n"; $sel = curl_multi_select($master, 5.0); //echo "selected $sel\n"; if ($sel === -1) { echo "sleeping\n"; usleep(1000 * 1); } $old_running = $running; do { $mrc = curl_multi_exec($master, $running); } while ($mrc == CURLM_CALL_MULTI_PERFORM);
/** * Add Handle * * @access private * @param $curl * @throws \ErrorException */ private function addHandle($curl) { $curlm_error_code = curl_multi_add_handle($this->multiCurl, $curl->curl); if (!($curlm_error_code === CURLM_OK)) { throw new \ErrorException('cURL multi add handle error: ' . curl_multi_strerror($curlm_error_code)); } $curl->beforeSend($this->beforeSendFunction); $curl->success($this->successFunction); $curl->error($this->errorFunction); $curl->complete($this->completeFunction); $this->curls[] = $curl; $curl->id = $this->nextCurlId++; if ($this->isStarted) { $this->initHandle($curl); } }
$channels = array(); foreach ($urls as $key => $url) { // initiate individual channel $channels[$key] = curl_init(); curl_setopt_array($channels[$key], array(CURLOPT_URL => $url, CURLOPT_RETURNTRANSFER => true, CURLOPT_FOLLOWLOCATION => true)); // add channel to multihandler curl_multi_add_handle($mh, $channels[$key]); } // execute - if there is an active connection then keep looping $active = null; do { $status = curl_multi_exec($mh, $active); // Check for errors if ($status > 0) { // Display error message echo "ERROR!\n " . curl_multi_strerror($status); } } while ($active && $status == CURLM_OK); // done! $time_end = microtime(true); $time = $time_end - $time_start; $download_size = 0; // echo the content, remove the handlers, then close them foreach ($channels as $chan) { //echo curl_multi_getcontent($chan); $download_size += strlen(curl_multi_getcontent($chan)); curl_multi_remove_handle($mh, $chan); curl_close($chan); } // --------------------------------------------------------- // Summary
/** * @param resource $curlHandle * @return DefaultConnectionResponse * @throws Exception */ private function executeCurlHandle($curlHandle) { $multiHandle = $this->getCurlMultiHandle(); curl_multi_add_handle($multiHandle, $curlHandle); $running = null; do { $status = curl_multi_exec($multiHandle, $running); if ($status > CURLM_OK) { $errorMessage = 'cURL error ' . $status; if (function_exists('curl_multi_strerror')) { $errorMessage .= ' (' . curl_multi_strerror($status) . ')'; } throw new ErrorException($errorMessage); } $info = curl_multi_info_read($multiHandle); if ($info && isset($info['result']) && $info['result'] != CURLE_OK) { $errorMessage = 'cURL error ' . $info['result']; if (function_exists('curl_strerror')) { $errorMessage .= ' (' . curl_strerror($info['result']) . ')'; } throw new ErrorException($errorMessage); } curl_multi_select($multiHandle); } while ($running > 0); $content = curl_multi_getcontent($curlHandle); $headerSize = curl_getinfo($curlHandle, CURLINFO_HEADER_SIZE); $httpCode = curl_getinfo($curlHandle, CURLINFO_HTTP_CODE); curl_multi_remove_handle($multiHandle, $curlHandle); $httpHeaderHelper = new HttpHeaderHelper(); $headers = $httpHeaderHelper->parseRawHeaders(explode("\r\n", substr($content, 0, $headerSize))); $body = substr($content, $headerSize); return new DefaultConnectionResponse($httpCode, $headers, $body); }
/** * Init Handle * * @access private * @param $curl * @throws \ErrorException */ private function initHandle($curl) { // Set callbacks if not already individually set. if ($curl->beforeSendFunction === null) { $curl->beforeSend($this->beforeSendFunction); } if ($curl->successFunction === null) { $curl->success($this->successFunction); } if ($curl->errorFunction === null) { $curl->error($this->errorFunction); } if ($curl->completeFunction === null) { $curl->complete($this->completeFunction); } foreach ($this->options as $option => $value) { $curl->setOpt($option, $value); } foreach ($this->headers as $key => $value) { $curl->setHeader($key, $value); } foreach ($this->cookies as $key => $value) { $curl->setCookie($key, $value); } $curl->setJsonDecoder($this->jsonDecoder); $curl->setXmlDecoder($this->xmlDecoder); $curlm_error_code = curl_multi_add_handle($this->multiCurl, $curl->curl); if (!($curlm_error_code === CURLM_OK)) { throw new \ErrorException('cURL multi add handle error: ' . curl_multi_strerror($curlm_error_code)); } $this->activeCurls[$curl->id] = $curl; $this->responseCookies = array(); $curl->call($curl->beforeSendFunction); }
/** @function process the actual process to send * @param $urls the url or array of urls to process required * @param $method the method to send pass the url (GET or POST) defaults to GET * @param $data the specific data to send (defaults to null) * @param $sendAsJSON whether to send the data in JSON or not faults to false * @param $auth whether to send an auth string. Defaults to false, other wise takes the actual auth string to pass * @param $callback whether to use a user passed callback to process. Defaults to false * @param $file whether we're downloading a file or not. Defaults to false, others takes the file resource * @return if $file it just closes the handle otherwise it returns the response */ public function process($urls, $method = false, $data = null, $sendAsJSON = false, $auth = false, $callback = false, $file = false) { if (!is_array($urls)) { $urls = array($urls); } foreach ($urls as $url) { $ProxyUsed = false; if ($this->DB && $this->FOGCore->getSetting('FOG_PROXY_IP')) { foreach ($this->getClass('StorageNodeManager')->find() as $StorageNode) { $IPs[] = $this->FOGCore->resolveHostname($StorageNode->get('ip')); } $IPs = array_filter(array_unique((array) $IPs)); if (!preg_match('#^(?!.*' . implode('|', (array) $IPs) . ')$#i', $url)) { $ProxyUsed = true; } $username = $this->FOGCore->getSetting('FOG_PROXY_USERNAME'); $password = $this->FOGCore->getSetting('FOG_PROXY_PASSWORD'); } if ($ProxyUsed) { $this->contextOptions[CURLOPT_PROXYAUTH] = CURLAUTH_BASIC; $this->contextOptions[CURLOPT_PROXYPORT] = $this->FOGCore->getSetting('FOG_PROXY_PORT'); $this->contextOptions[CURLOPT_PROXY] = $this->FOGCore->getSetting('FOG_PROXY_IP'); if ($username) { $this->contextOptions[CURLOPT_PROXYUSERPWD] = $username . ':' . $password; } } if ($method == 'GET' && $data !== null) { $url .= '?' . http_build_query($data); } $ch = @curl_init($url); $this->contextOptions[CURLOPT_URL] = $url; if ($auth) { $this->contextOptions[CURLOPT_USERPWD] = $auth; } if ($file) { $this->contextOptions[CURLOPT_FILE] = $file; $this->contextOptions[CURLOPT_TIMEOUT_MS] = 300000000; } if ($method == 'POST' && $data !== null) { if ($sendAsJSON) { $data = json_encode($data); $this->contextOptions[CURLOPT_HTTPHEADER] = array('Content-Type: application/json', 'Content-Length: ' . strlen($data)); } $this->contextOptions[CURLOPT_POSTFIELDS] = $data; } $this->contextOptions[CURLOPT_CUSTOMREQUEST] = $method; curl_setopt_array($ch, $this->contextOptions); $curl[$url] = $ch; @curl_multi_add_handle($this->handle, $ch); } $active = null; do { $mrc = @curl_multi_exec($this->handle, $active); } while ($mrc == CURLM_CALL_MULTI_PERFORM); while ($active && $mrc == CURLM_OK) { if (@curl_multi_select($this->handle) == -1) { usleep(1); } do { $mrc = @curl_multi_exec($this->handle, $active); $httpCode = @curl_multi_info_read($this->handle); if ($mrc > 0) { throw new Exception('cURL Error: ' . curl_multi_strerror($mrc)); } if ($httpCode[0] >= 400) { @curl_multi_close($this->handle); throw new Exception('cURL HTTP Error Code: ' . $httpCode[0]); } } while ($mrc == CURLM_CALL_MULTI_PERFORM); } if (!$file) { foreach ($curl as $url => $ch) { if ($callback) { $callback($ch); } $response[] = @curl_multi_getcontent($ch); @curl_multi_remove_handle($this->handle, $ch); } return $response; } else { @fclose($file); } }
/** * wait for all responses * * $wait_function indicates like this: * * function wait_func($total_wait, $function){ * // $total_wait: total wait time in msec * // $function: function name which needs wait * // return value: if you want to break waitForAll, return true. * if ( $total_wait > 2000 ) return true; // will cancel waitForAll(throws UserCancelException) * usleep(20); * return false; // continue execution of waitForAll * } * * @param callable $wait_function user defined wait function.if this set to null, default wait ant default * timeout will be applied. * * @return array * * @throws UserCancelException */ public function waitForAll($wait_function = null) { $mh = new CurlMultiHandle(); $mh->setOptions($this->options); foreach ($this->requests as $req) { $cho = $this->pool->acquireObject(); $cho->setRequest($req); $mh->addHandle($cho); } $result = []; $total_wait = 0; do { $mh->select(); $stat = $mh->execute($running); if ($running) { $start = microtime(true); if ($wait_function && is_callable($wait_function)) { $canceled = call_user_func_array($wait_function, [$total_wait, 'curl_multi_exec']); if ($canceled) { throw new UserCancelException(); } } else { // default wait if ($total_wait + self::DEFAULT_SLEEP_WAIT > self::DEFAULT_WAIT_TMEOUT) { throw new TimeoutException(); } usleep(self::DEFAULT_SLEEP_WAIT); } $total_wait += microtime(true) - $start; continue; } } while ($stat === CURLM_CALL_MULTI_PERFORM || $running); if ($stat !== CURLM_OK) { $errmsg = curl_multi_strerror($stat); throw new GrasshopperException('curl_multi_exec failed:' . $errmsg, self::ERROR_MULTI_EXEC); } // read each response do { $res = $mh->getInfo($remains); if (!$res) { $start = microtime(true); if ($wait_function && is_callable($wait_function)) { $canceled = call_user_func_array($wait_function, [$total_wait, 'curl_multi_info_read']); if ($canceled) { throw new UserCancelException(); } } else { // default wait if ($total_wait + self::DEFAULT_SLEEP_WAIT > self::DEFAULT_WAIT_TMEOUT) { throw new TimeoutException(); } usleep(self::DEFAULT_SLEEP_WAIT); } $total_wait += microtime(true) - $start; $remains = 1; continue; } $response = null; /** @var resource $ch */ $ch = $res['handle']; // find cURL handle object $cho = $this->pool->findObject($ch); /** @var CurlRequest $request */ $request = $cho->getRequest(); /** @var string $url */ $request_url = $request->getUrl(); if ($res['result'] !== CURLE_OK) { $errno = $res['result']; $function = 'curl_multi_info_read'; $error = new CurlError($errno, $function); goto REQUEST_FAILED; } $info = curl_getinfo($ch); if ($info === false) { $errno = curl_errno($ch); $function = 'curl_getinfo'; $error = new CurlError($errno, $function); goto REQUEST_FAILED; } $effective_url = curl_getinfo($ch, CURLINFO_EFFECTIVE_URL); if ($effective_url === false) { $errno = curl_errno($ch); $function = 'curl_getinfo'; $error = new CurlError($errno, $function); goto REQUEST_FAILED; } $info['effective_url'] = $effective_url; $fp = $request->getFileHandle(); fseek($fp, 0); $content = fread($fp, $this->max_download_size); fclose($fp); REQUEST_SUCCEEDED: $response = null; try { $response = new CurlResponse($info, $content); } catch (DeflateException $ex) { $error = new HttpError(0, 'failed to deflate'); goto REQUEST_FAILED; } $event = new SuccessEvent($request, $response); // callback $request->onRequestSucceeded($event); if ($this->complete_callback) { call_user_func_array($this->complete_callback, [$event]); } $result["{$request_url}"] = $event; goto REQUEST_FINISH; REQUEST_FAILED: $event = new ErrorEvent($request, $error, $response); // callback $request->onRequestFailed($event); if ($this->error_callback) { call_user_func_array($this->error_callback, [$event]); } $result["{$request_url}"] = $event; goto REQUEST_FINISH; REQUEST_FINISH: $mh->removeHandle($cho); $this->pool->releaseObject($cho); } while ($remains); $mh->close(); return $result; }
/** * Starts a session by sending out the added requests. * * @param reference $success **OPTIONAL. OUTPUT.** After the method is called with this parameter provided, the * parameter's value tells whether the session was successful. * * @return void */ public function start(&$success = null) { $success = true; if ($this->m_hasError) { $success = false; return; } if (CArray::isEmpty($this->m_requestRecordsQueue)) { // Nothing to do. return; } // Current policy is to disable HTTP pipelining. $res = curl_multi_setopt($this->m_multiCurl, CURLMOPT_PIPELINING, 0); if (!$res) { // Should never get in here as long as cURL options are being set correctly, hence the assertion. assert('false', vs(isset($this), get_defined_vars())); $this->m_hasError = true; $this->m_errorMessage = "The 'curl_multi_setopt' function failed."; $success = false; $this->finalize(); return; } $anySuccessfulRequests = false; // Disable the script's execution timeout before getting into the session. $timeoutPause = new CTimeoutPause(); $numRunningRequests = 0; // also the index of the next request to send while (true) { // From the request queue, add as many normal cURL handles to the multi cURL handle as it is allowed by the // maximum number of concurrent requests, priorly setting internal options for every request. while ($numRunningRequests < CArray::length($this->m_requestRecordsQueue) && $numRunningRequests < $this->m_maxNumConcurrentRequests) { $requestRecord = $this->m_requestRecordsQueue[$numRunningRequests]; $request = $requestRecord[0]; $onCompleteCallback = $requestRecord[1]; $newCookieSession = $requestRecord[2]; $requestCurl = $request->curl(); // Set cURL options for the normal cURL handle, having created a temporary file for cookie storage if // needed. $requestSetOptSuccess; if ($this->m_cookiesAreEnabled && $request->isHttp()) { if (!isset($this->m_cookiesFp)) { $this->m_cookiesFp = CFile::createTemporary(); } $request->setInternalOptions($requestSetOptSuccess, $this->m_cookiesFp, $newCookieSession); } else { $request->setInternalOptions($requestSetOptSuccess); } if (!$requestSetOptSuccess) { if (isset($onCompleteCallback)) { call_user_func($onCompleteCallback, false, "", $request, $this); } CArray::remove($this->m_requestRecordsQueue, $numRunningRequests); continue; } // Add the normal cURL handle to the multi cURL handle. $res = curl_multi_add_handle($this->m_multiCurl, $requestCurl); if ($res != 0) { $this->m_hasError = true; $curlError = curl_multi_strerror($res); $this->m_errorMessage = is_cstring($curlError) && !CString::isEmpty($curlError) ? $curlError : "The 'curl_multi_add_handle' function failed."; $success = false; $timeoutPause->end(); $this->finalize(); return; } $numRunningRequests++; } if ($numRunningRequests == 0) { break; } // Process the currently added requests until complete or no more data is available. Although // `CURLM_CALL_MULTI_PERFORM` is deprecated since libcurl 7.20, keep it for compatibility reasons. $numRunningTransfers; do { $multiExecRes = curl_multi_exec($this->m_multiCurl, $numRunningTransfers); } while ($multiExecRes == CURLM_CALL_MULTI_PERFORM); if ($multiExecRes != CURLM_OK) { $this->m_hasError = true; $curlError = curl_multi_strerror($multiExecRes); $this->m_errorMessage = is_cstring($curlError) && !CString::isEmpty($curlError) ? $curlError : "The 'curl_multi_exec' function failed."; $success = false; $timeoutPause->end(); $this->finalize(); return; } // Check for completed requests, call the callback function for any completed one (if such a function is // defined), finalize completed requests, and remove completed requests from the queue. while (true) { $completedRequestInfo = curl_multi_info_read($this->m_multiCurl); if (!is_cmap($completedRequestInfo)) { break; } // A request has completed. assert('$completedRequestInfo["msg"] == CURLMSG_DONE', vs(isset($this), get_defined_vars())); $requestCurl = $completedRequestInfo["handle"]; $requestRes = $completedRequestInfo["result"]; $requestRecordPos; $found = CArray::find($this->m_requestRecordsQueue, $requestCurl, function ($requestRecord, $requestCurl) { $request = $requestRecord[0]; return $request->curl() == $requestCurl; }, $requestRecordPos); assert('$found', vs(isset($this), get_defined_vars())); $requestRecord = $this->m_requestRecordsQueue[$requestRecordPos]; $request = $requestRecord[0]; $onCompleteCallback = $requestRecord[1]; // Remove the normal cURL handle from the multi cURL handle. $res = curl_multi_remove_handle($this->m_multiCurl, $requestCurl); if ($res != 0) { $this->m_hasError = true; $curlError = curl_multi_strerror($res); $this->m_errorMessage = is_cstring($curlError) && !CString::isEmpty($curlError) ? $curlError : "The 'curl_multi_remove_handle' function failed."; $success = false; $timeoutPause->end(); $this->finalize(); return; } if ($requestRes == CURLE_OK) { // The request has succeeded. if (isset($onCompleteCallback)) { $response; if ($request->isReturnTransferSet()) { $response = curl_multi_getcontent($requestCurl); assert('is_cstring($response)', vs(isset($this), get_defined_vars())); } else { $response = ""; } $request->onRequestCompleteOk(); // also close the normal cURL handle call_user_func($onCompleteCallback, true, $response, $request, $this); } else { $request->onRequestCompleteOk(); // also close the normal cURL handle } $anySuccessfulRequests = true; } else { // The request has failed. $curlError = curl_strerror($requestRes); if (!is_cstring($curlError)) { $curlError = ""; } $request->onRequestCompleteWithError($curlError); // also close the normal cURL handle if (isset($onCompleteCallback)) { call_user_func($onCompleteCallback, false, "", $request, $this); } } CArray::remove($this->m_requestRecordsQueue, $requestRecordPos); $numRunningRequests--; } assert('$numRunningRequests == $numRunningTransfers', vs(isset($this), get_defined_vars())); if ($numRunningTransfers > 0) { // Some requests are still being processed (by remote machines). Wait for more data to appear on // sockets, without getting hard on the CPU. do { $multiSelectRes = curl_multi_select($this->m_multiCurl); } while ($multiSelectRes == -1); } else { // No requests are being processed. Check if any requests are pending. if (CArray::isEmpty($this->m_requestRecordsQueue)) { // No requests are pending. break; } } } // Set the script's execution time limit like the session has never happened. $timeoutPause->end(); if (!$anySuccessfulRequests) { $this->m_hasError = true; $this->m_errorMessage = "None of the session's requests succeeded."; $success = false; } $this->finalize(); }