SSL Requests made with cURL fail after process fork

坚强是说给别人听的谎言 提交于 2019-12-24 02:33:13

问题


I have run into some pretty strange behaviour with curl

  1. If I make an SSL request using curl in the parent process and then fork the process and try to make another SSL request in the child process the attempt fails with error no. 35 (SSL connect error).
  2. If I do not make the SSL request in the parent, the one in the child process succeeds.
  3. I can make any number of non SSL requests in the parent and SSL requests in the child succeed.

It appears that this is a bug in libcurl related question and the answerer has a work around for it.

My questions are:

  1. Is curl_global_cleanup exposed by some other name in the PHP API?
  2. If not is there some other work around?

$ch = curl_init('https://www.google.ca/');
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, 0);
curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, 0);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
$success = curl_exec($ch);
var_dump($success !== false); // true
curl_close($ch); 

$pid = pcntl_fork();

if ($pid === 0) {
    $ch = curl_init('http://www.google.ca/');
    curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
    $success = curl_exec($ch);
    var_dump($success !== false); // true
    curl_close($ch);

    $ch = curl_init('https://www.google.ca/');
    curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, 0);
    curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, 0);
    curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
    $success = curl_exec($ch);
    var_dump($success !== false); // false

    $errno = curl_errno($ch); // 35
    $error = curl_error($ch); // SSL connect error
    curl_close($ch);
} else if ($pid > 0) {
    // wait for child process
    pcntl_wait($status);
} else {
    // handel fork error
}

If this is not a bug with libcurl and it is something I am doing wrong please let me know.


回答1:


I just solved this by using the pcntl_exec function. So you would essentially remove your child code and place it in a different php file which then gets executed in (and actually replaces) the child process.

$ch = curl_init('https://www.google.ca/');
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, 0);
curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, 0);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
$success = curl_exec($ch);
var_dump($success !== false); // true
curl_close($ch); 

$pid = pcntl_fork();

if ($pid === 0) {
  pcntl_exec('child_curl.php');
} else if ($pid > 0) {
  // wait for child process
  pcntl_wait($status);
} else {
  // handel fork error
}

Contents of child_curl.php:

$ch = curl_init('http://www.google.ca/');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
$success = curl_exec($ch);
var_dump($success !== false); // true
curl_close($ch);

$ch = curl_init('https://www.google.ca/');
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, 0);
curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, 0);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
$success = curl_exec($ch);
var_dump($success !== false); // false

$errno = curl_errno($ch); // 35
$error = curl_error($ch); // SSL connect error
curl_close($ch);

After the execution of the child you will not see an error.

These other issues helped me out:

  • libCurl SSL error after fork()
  • Curl and pcntl_fork()



回答2:


Another workaround is to use sockets.

function fetch_page($url) {
    $socketPair = array();
    if (socket_create_pair(AF_UNIX, SOCK_STREAM, 0, $socketPair) === false) {
        return false;
    }
    $pid = pcntl_fork();
    if ($pid === 0) {
        $ch = curl_init($url);
        curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, 0);
        curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, 0);
        curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
        $content = curl_exec($ch);
        curl_close($ch);
        socket_close($socketPair[1]);
        socket_set_nonblock($socketPair[0]);
        $exitStatus = $content ? 0 : 1;
        if ($content) {
            while ((strlen($content) > 0) && ($wrote = socket_write($socketPair[0], $content))) {
                $content = substr($content, $wrote);
            }
        }
        socket_close($socketPair[0]);
        exit($exitStatus);
    }
    else if ($pid > 0) {
        pcntl_wait($status);
        socket_close($socketPair[0]);
        $content = false;
        if (pcntl_wexitstatus($status) === 0) {
            $content = '';
            while ($line = socket_read($socketPair[1], 4096)) {
                $len = strlen($content);
                $content .= $line;
            }
        }
        socket_close($socketPair[1]);
        return $content;
    }
    socket_close($socketPair[0]);
    socket_close($socketPair[1]);
    return false;
} 



回答3:


I have not found a way to directly deal with the issue out lined in the question. However, I have developed a couple of workarounds. Both solve the process fork problem with (guess what) another fork.

Please note: This is not intended to be any kind of performance improvement. It is simply a workaround for the issue highlighted in the question.

function fetch_page($page) {
    $tmp_file = tempnam(sys_get_temp_dir(), 'curl_tmp_file');
    $pid = pcntl_fork();
    if ($pid === 0) {
        $ch = curl_init($page);
        curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, 0);
        curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, 0);
        curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
        $content = curl_exec($ch);
        curl_close($ch);
        if ($content) {
            file_put_contents($tmp_file, $content);
            exit(0);
        }
        else {
            exit(1);
        }
    }
    else if ($pid > 0) {
        pcntl_wait($status);
        $content = false;
        if (pcntl_wexitstatus($status) === 0) {
            $content = file_get_contents($tmp_file);
        }
        unlink($tmp_file);
        return $content;
    }
    else {
        unlink($tmp_file);
        return false;
    }
}

Letter on I can make https connections in a child process.

fetch_page('https://www.google.ca/');

$pid = pcntl_fork();
if ($pid === 0) {   
    $ch = curl_init('https://www.google.ca/');
    curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, 0);
    curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, 0);
    curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
    $success = curl_exec($ch);
    var_dump($success !== false); // true
} else if ($pid > 0) {
    // wait for child process
    pcntl_wait($status);
} else {
    // handel fork error
}


来源:https://stackoverflow.com/questions/26285311/ssl-requests-made-with-curl-fail-after-process-fork

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!