1

In PHP, curl_exec there are multiple options that you can use to abort a request that is taking too long, CURLOPT_TIMEOUT, CURLOPT_CONNECTTIMEOUT, CURLOPT_LOW_SPEED_TIME etc. I believe the errno for all these cases is 28 (which just indicates that the operation timed out), does libcurl report anything different depending on which timeout was reached, and does PHP expose this functionality?

I tried altering the values of these options on a simple PHP function, but wasn't able to generate a non-28 errno.

function curl_test() {

    $ch = curl_init();

    $options = array(
        CURLOPT_URL => 'http://www.google.com',
        CURLOPT_HEADER => false,
        CURLOPT_TIMEOUT_MS => 1000,
        CURLOPT_CONNECTTIMEOUT_MS => 10,
    );

    curl_setopt_array($ch, $options);

    curl_exec($ch);

    $out = curl_errno($ch);
    curl_close($ch);
    return $out;
}
mpromonet
  • 11,326
  • 43
  • 62
  • 91
Greg Nisbet
  • 6,710
  • 3
  • 25
  • 65
  • 1
    I think you mean that you want to know which Timeout limit was reach, right? Libcurl cannot determine why a host has timed out, just that the limit was reached. – Twisty Oct 23 '15 at 18:21
  • Right, I'm not trying to collect information that would be impossible to gather about why the host has timed out. I just want to know which of the curl-specific timeouts was triggered in this case. – Greg Nisbet Oct 23 '15 at 18:26
  • So if you look at http://curl.haxx.se/libcurl/c/libcurl-errors.html, Error No. 28 is: `CURLE_OPERATION_TIMEDOUT (28)`, It looks like curl lumps all timeouts into this error. I don't see a solid way to determine which operation threw the error from the error reporting in curl. So we have to add a flag to know what curl is doing when the time out is encountered. Will move an example to an answer. – Twisty Oct 23 '15 at 18:32

1 Answers1

2

To accomplish this, I think we need to simply test for the Timeout we're looking for. So I see the following scenarios:

  1. Timeout connecting to the URL (Connection Time Out - Host down)
  2. Timeout performing the operation (Operation Time Out)
  3. Timeout due to Slow connection (Low Speed Time)

We can check for the first timeout using other methods. So lets do that:

<?php
$site="http://www.google.com";
$content = file_get_contents($site);
if($content === false){
    // Host dead, handle error here...
}
?>

Reference: How can I handle the warning of file_get_contents() function in PHP?

At this point, we know that we should not get a connection timeout. We can proceed with the curl execution and know that it will be an operation timeout or speed.

<?php
function curl_run($opts){
    $content = file_get_contents($opts[CURLOPT_URL]);
    if($content === false){
        return false;
    } else {
        $resp = array();
        $time_start = microtime(true);
        $ch = curl_init();
        curl_setopt_array($ch, $opts);
        $resp['result'] = curl_exec($ch);
        $time_end = microtime(true);
        $resp['info'] = curl_getinfo($ch);
        if(curl_errno($ch)){
            $resp['error'] = curl_error($ch);
        }
        curl_close($ch);
        $resp['time'] = $time_end - $time_start; // Time it took
        return $resp;
    }
}

$url = isset($_GET['url'])?$_GET['url']:"http://www.google.com/";
$options = array(
        CURLOPT_URL => $url,
        CURLOPT_HEADER => true,
        CURLOPT_TIMEOUT_MS => 1000,
        CURLOPT_CONNECTTIMEOUT_MS => 10,
        CURLOPT_RETURNTRANSFER => 1,
        CURLOPT_VERBOSE => 1
);

$results = curl_run($options);
if($results){
        echo "<h2>Options</h2>\r\n";
        echo "<pre>";
        foreach($options as $k => $v){
                echo "$k: $v\r\n";
        }
        echo "</pre>";
        echo "<h2>Info</h2>\r\n";
        echo "<pre>";
        foreach($results['info'] as $k => $v){
                echo "$k: $v\r\n";
        }
        echo "time: {$results['time']}\r\n";
        echo "</pre>\r\n";
        if(isset($results['error'])){
                echo "<h2>Error</h2>\r\n";
                echo "<pre>{$results['error']}</pre>\r\n";
        }
        echo "<h2>Response</h2>\r\n";
        echo "<pre>" . htmlentities($results['result']) . "</pre>";
}
?>

We should be able to look at all of that and determine if the time it took to execute the operation was then close to one of our Timeout settings. Otherwise we would also have lots of data under info too. If it returns false, we know the host was unreachable.

Did further testing, and you can see them here: http://www.yrmailfrom.me/projects/php/curl_test1.php?url=http://www.apple.com/

I found that Apple.com was taking 14 ms, so it became a good test.

Example output:

Options

10002: http://www.apple.com/
42: 1
155: 1000
156: 10
19913: 1
41: 1

Info

url: http://www.apple.com/
content_type: 
http_code: 0
header_size: 0
request_size: 0
filetime: -1
ssl_verify_result: 0
redirect_count: 0
total_time: 0.014634
namelookup_time: 0.004297
connect_time: 0
pretransfer_time: 0
size_upload: 0
size_download: 0
speed_download: 0
speed_upload: 0
download_content_length: -1
upload_content_length: -1
starttransfer_time: 0
redirect_time: 0
redirect_url: 
primary_ip: 
certinfo: Array
primary_port: 0
local_ip: 
local_port: 0
time: 0.014760017395

Error

Connection timed out after 14 milliseconds

Response
Community
  • 1
  • 1
Twisty
  • 30,304
  • 2
  • 26
  • 45