curl_exec 区分超时原因

curl_exec distinguish cause of timeout

在 PHP、curl_exec 中有多个选项可用于中止耗时过长的请求,CURLOPT_TIMEOUTCURLOPT_CONNECTTIMEOUTCURLOPT_LOW_SPEED_TIME 等。我相信所有这些情况的 errno 都是 28(这只是表示操作超时),libcurl 是否根据达到的超时报告任何不同,PHP 是否公开此功能?

我尝试在一个简单的 PHP 函数上更改这些选项的值,但无法生成非 28 错误码。

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;
}

为了完成这个,我认为我们需要简单地测试我们正在寻找的超时。所以我看到了以下场景:

  1. 连接到 URL 超时(连接超时 - 主机关闭)
  2. 执行操作超时(Operation Time Out)
  3. 由于连接速度慢(低速时间)导致超时

我们可以使用其他方法检查第一次超时。那么让我们这样做:

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

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

至此,我们知道我们不应该连接超时。我们可以继续curl执行,知道会是操作超时或者速度。

<?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>";
}
?>

我们应该能够查看所有这些并确定执行操作所花费的时间是否接近我们的超时设置之一。否则我们在 info 下也会有很多数据。如果它 returns 为假,我们就知道主机无法访问。

做了进一步的测试,你可以在这里看到它们:http://www.yrmailfrom.me/projects/php/curl_test1.php?url=http://www.apple.com/

我发现 Apple.com 花了 14 毫秒,所以它成为一个很好的测试。

示例输出:

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