Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Detect cURL timeout in PHP

Tags:

php

curl

I'm trying to detect when a cURL request times out. I'm using curl_multi_exec if this makes a difference?

The output of curl_errno() is 0 which suggests it was a success. However the output of curl_error() is:

Operation timed out after 1435 milliseconds with 0 out of -1 bytes received

Any ideas why the error code is good, but the error message exists? I would expect an error code of 28 for a timeout.

Also, is there anything I can check in curl_getinfo() for a timeout?

I'm using PHP 5.4.4 / cURL 7.24.0.

Edit 1 - Sample code:

$mh = curl_multi_init();
curl_multi_add_handle($mh,$a);
curl_multi_add_handle($mh,$b);
curl_multi_add_handle($mh,...);

do {
    $mrc = curl_multi_exec($mh, $active);
} while ($mrc == CURLM_CALL_MULTI_PERFORM);

while ($active && $mrc == CURLM_OK) {
    if (curl_multi_select($mh) == -1) usleep(100);
    do { $mrc = curl_multi_exec($mh, $active); }
    while ($mrc == CURLM_CALL_MULTI_PERFORM);
}
like image 733
Kit Avatar asked May 12 '14 15:05

Kit


1 Answers

When using curl_multi_exec(), you'll need to use curl_multi_info_read() to get the error code for specific handles. This is due to the way PHP interfaces with cURL in its easy and multiple interfaces, and how error codes are fetched on individual handles from cURL's curl_multi_info_read() function (see explanation below).

Basically, if you are using multi handles, calling curl_errno() and curl_error() are not reliable or accurate.

See this modified example from the manual:

<?php

$urls = array(
   "http://www.cnn.com/",
   "http://www.bbc.co.uk/",
   "http://www.yahoo.com/",
   'http://wijgeiwojgieowjg.com/',
   'http://www.example.com/',
);

$infos = array();

$mh = curl_multi_init();

foreach ($urls as $i => $url) {
    $conn[$i] = curl_init($url);
    curl_setopt($conn[$i], CURLOPT_RETURNTRANSFER, 1);

    if (strpos($url, 'example.com') !== false) {
        // set a really short timeout for one url
        curl_setopt($conn[$i], CURLOPT_TIMEOUT_MS, 10);
    }

    curl_multi_add_handle($mh, $conn[$i]);
}

do {
    $status = curl_multi_exec($mh, $active);

    if (($info = curl_multi_info_read($mh)) !== false) {
        $infos[$info['handle']] = $info;
    }

} while ($status === CURLM_CALL_MULTI_PERFORM || $active);

foreach ($urls as $i => $url) {
    $info = $infos[$conn[$i]];

    echo "$url returned code {$info['result']}";
    if (version_compare(PHP_VERSION, '5.5.0') >= 0) {
        echo ": " . curl_strerror($info['result']);
    }
    echo "\n";

    if ($info['result'] === 0) {
        $res[$i] = curl_multi_getcontent($conn[$i]);
    }

    curl_close($conn[$i]);
}

Output:

http://www.cnn.com/ returned code 0

http://www.bbc.co.uk/ returned code 0

http://www.yahoo.com/ returned code 0

http://wijgeiwojgieowjg.com/ returned code 6

http://www.example.com/ returned code 28

Explanation:

Specifically, this is due to how PHP's curl_exec() calls cURL's curl_easy_perform which returns a CURLcode (error code) and PHP specifies the cURL option CURLOPT_ERRORBUFFER which causes a buffer to automatically get filled with an error message if one occurs.

But when using PHP's curl_multi_exec(), PHP calls cURL's curl_multi_perform which returns immediately and doesn't return error codes for the multi handles. You must call cURL's curl_multi_info_read function to get error codes for the individual handles.

PHP 5.5.0 provides a wrapper to cURL's curl_easy_strerror() which returns a string corresponding to a curl error code.

like image 138
drew010 Avatar answered Nov 18 '22 02:11

drew010