curl로 https 접속시 SSL connect error (errno=35) 발생.

여기저기 찾아보면서 nss 업데이트도 해보고 해도 해결이 안됨.

 

좀 더 자세히 알아보기 위해 단순 curl_error()가 아닌 디버깅 정보를 출력해보기로 함 (=curl -v)

참고: https://bloodguy.tistory.com/entry/PHP-curl-vverbose-%EC%98%B5%EC%85%98-%EC%84%B8%ED%8C%85-%EB%B0%8F-%EA%B2%B0%EA%B3%BC-%ED%99%95%EC%9D%B8%ED%95%98%EA%B8%B0

 

 

얻은 결과는 아래와 같음.

에러는 NSS error -8023

* About to connect() to DOMAIN port 443 (#0)
*   Trying IP_ADDR... * connected
* Connected to DOMAIN (IP_ADDR) port 443 (#0)
* warning: ignoring value of ssl.verifyhost
* NSS error -8023
* Closing connection #0
* SSL connect error

 

이 정보를 바탕으로 뒤져본 결과 에러발생 요건은 아래와 같음.

 

1. openssl이 아닌 nss와 연결된 libCurl 사용

2. parent process에서 curl 한 번 호출

3. 이후 fork()로 생성된 child process에서 curl 호출

 

위 3가지 조합이 전부 맞물려야 발생하는 에러임.

 

PHP 코드로 보자면 아래와 같음.

<?PHP                                                                                              
 
 
// curl 호출 함수                                                                                                   
function curlGet($procType)                                                                        
{                                                                                                  
    $verboseStream = fopen('php://temp', 'w+');                                                    
                                                                                                     
    $c = curl_init();                                                                              
    curl_setopt_array($c, array(                                                                   
        CURLOPT_URL => 'https://www.google.com',                                                   
        CURLOPT_RETURNTRANSFER => true,                                                            
        CURLOPT_VERBOSE => true,                                                                   
        CURLOPT_STDERR => $verboseStream                                                           
    ));                                                                                            
                                                                                                     
    $r = curl_exec($c);                                                                            
                                                                                                     
    rewind($verboseStream);                                                                        
    $verbose = stream_get_contents($verboseStream);                                                
                                                                                                     
    printf("[%s]\n%s\n%s\n", $procType, str_repeat('-', 120), $verbose);                           
}                                                                                                  
                                                                                                     
                                                                                                     
// 1. parent에서 curl 호출 (성공)                                                                  
curlGet('PARENT');                                                                                 
 
// 2. fork
$pid = pcntl_fork();
 
if ($pid === 0) {
    // 3. child에서 curl 호출 (에러 발생!!!!!!!!!!!!!!!!)
    curlGet('CHILD');
}
else if ($pid > 0) {
    // parent. child process 실행완료까지 대기
    pcntl_waitpid(0, $status);
}
else {
    // error
}

 

해결방법은 fork() 전에 curl_global_cleanup() 을 호출해주고 fork() 후에 curl_global_init() 를 호출해주는 것인 듯 한데,

PHP에선 어떻게 제어할 방법이 없다.

하지만 저걸 다른식으로 해주는 방법이 존재함.

fork() 후 child process에서 curl을 곧바로 호출하지 않고 pcntl_exec()를 이용해 다른 php 파일을 불러와서 실행하면 에러가 안남.

 

위의 예제를 기준으로 하자면 아래처럼 하면 됨.

우선 curlGet() 함수를 func.php 파일로 분리하고,

<?PHP                                                                                              
                                                                                                     
function curlGet($procType)                                                                        
{                                                                                                  
    $verboseStream = fopen('php://temp', 'w+');                                                    
                                                                                                     
    $c = curl_init();                                                                              
    curl_setopt_array($c, array(                                                                   
        CURLOPT_URL => 'https://www.google.com',                                                   
        CURLOPT_RETURNTRANSFER => true,                                                            
        CURLOPT_VERBOSE => true,                                                                   
        CURLOPT_STDERR => $verboseStream                                                           
    ));                                                                                            
                                                                                                     
    $r = curl_exec($c);                                                                            
                                                                                                     
    rewind($verboseStream);                                                                        
    $verbose = stream_get_contents($verboseStream);                                                
                                                                                                     
    printf("[%s]\n%s\n%s\n", $procType, str_repeat('-', 120), $verbose);                           
}

 

child process에서 실행될 파일도 child.php로 분리하고, (파일명 호출로 실행될 수 있도록 permission도 조정하고 인터프리터도 추가)

#!/PATH/TO/php -q
<?PHP
require_once __DIR__.'/func.php';
 
 
curlGet('CHILD');

 

parent process는 아래처럼.

<?PHP
require_once __DIR__.'/func.php';
 
 
// 1. parent에서 curl 호출 (성공)
curlGet('PARENT');
 
// 2. fork
$pid = pcntl_fork();
 
if ($pid === 0) {
    // 3. child에서 curl 호출 (성공)
    pcntl_exec(__DIR__.'/child.php');
}
else if ($pid > 0) {
    pcntl_waitpid(0, $status);
}
else {
    // error
}

 

이제 에러가 안나고 정상동작 함.

 

 

 

 

.

Posted by bloodguy
,