使用cURL进行的SSL请求在进程分叉后失败

时间:2014-10-09 18:14:35

标签: php ssl curl centos fork

我在curl中遇到了一些非常奇怪的行为

  1. 如果我在父进程中使用curl发出SSL请求,然后 分叉进程并尝试在子进程中发出另一个SSL请求 处理尝试失败,错误号。 35(SSL连接错误)。
  2. 如果我没有在父级中发出SSL请求,则子进程中的SSL请求会成功。
  3. 我可以在父级中生成任意数量的非SSL请求,并且子级中的SSL请求成功。
  4. 这似乎是libcurl related question中的一个错误,而回答者有一个work around for it

    我的问题是:

    1. PHP API中的其他名称是否显示curl_global_cleanup
    2. 如果没有,还有其他工作吗?
    3. $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
      }
      

      如果这不是libcurl的错误而且我做错了请告诉我。

3 个答案:

答案 0 :(得分:1)

我刚刚使用pcntl_exec函数解决了这个问题。所以你基本上会删除你的子代码并将它放在一个不同的php文件中,然后执行(并实际替换)子进程。

public class SomeClass() {
    private ISomeService _service;
    private IAnotherService _anotherService;
    public SomeClass(ISomeService service, IAnotherService anotherService) {
        _service = service;
        _anotherService = anotherService;
    }
}

child_curl.php的内容:

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

执行孩子后,你不会看到错误。

这些其他问题帮助了我:

答案 1 :(得分:0)

我还没有找到一种直接处理问题的方法。但是,我已经开发了几个解决方法。两者都解决了进程fork问题(猜猜是什么)另一个fork。

请注意:这不是为了提高性能。这只是问题中突出显示的问题的解决方法。

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

关于我的信可以在子进程中建立https连接。

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
}

答案 2 :(得分:0)

另一种解决方法是使用套接字。

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