mgutt
mgutt

Reputation: 6177

file_get_contents() does not timeout / return error with HTTPS urls

I do not know why, but I have one specific URL that does not timeout (tested several thousand others without problems):

<?php
$url = 'https://....';
$context = stream_context_create(array(
    'http' => array(
        'follow_location' => false,
        'timeout' => 2,
    )
));
file_get_contents($url, false, $context);
?>

If I set follow_location to true it works without problems.

Update 1
The call of file_get_contents does not respect the 'timeout' => 2 and it does not respect PHP max_execution_time, but there seems to be a third timeout on the server itself. It returns an Internal Server Error 500 after 10 (!) minutes.

Update 2
I tried the same URL with cURL and there is no timeout problem but it returns false:

<?php
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, $url);
curl_setopt($ch, CURLOPT_HEADER, true);
curl_setopt($ch, CURLOPT_FOLLOWLOCATION, false);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
$contents = curl_exec($ch);
var_dump($contents);
?>

What is needed to let cURL return false?

Update 3
Ok now I catched it:

if ($contents === false) {
    echo curl_error($ch) . ' (' . curl_errno($ch) . ')' . PHP_EOL;
}

returns:

SSL certificate problem: unable to get local issuer certificate (60)

This means every time this error occurs file_get_contents() does not respect an timeout if follow_location is set to false. Sounds like a bug for me.

Update 4
As @huggilou suggested I tried several additional settings and after setting this I'm able to load the URL:

curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, 0);

Is there a similar setting for file_get_contents()?

Update 5

Now I tried the following:

$context = stream_context_create(array(
    'http' => array(
        'follow_location' => false,
        'timeout' => 2,
    ),
    'ssl' => array(
        'verify_peer' => false,
    ),
));
echo file_get_contents($url, false, $context);

This caused the timeout problem. After that I changed verify_peer to true and got this result:


Warning: file_get_contents(): SSL operation failed with code 1. OpenSSL Error messages: error:14090086:SSL routines:SSL3_GET_SERVER_CERTIFICATE:certificate verify failed in /usr/www/users//t051.php on line 12

Warning: file_get_contents(): Failed to enable crypto in /usr/www/users//t051.php on line 12

Warning: file_get_contents(https://...): failed to open stream: operation failed in /usr/www/users//t051.php on line 12

By that we know: verfiy_peer is set to false by default.

And to remind: If I set verify_peer to false and follow_location to true the website is loaded!?

Update 6
As @huggilou pointed to that:

<?php
$w = stream_get_wrappers();
echo 'allow_url_fopen: ', ini_get('allow_url_fopen') ? 'yes':'no', PHP_EOL;
echo 'openssl: ',  extension_loaded('openssl') ? 'yes':'no', PHP_EOL;
echo 'http wrapper: ', in_array('http', $w) ? 'yes':'no', PHP_EOL;
echo 'https wrapper: ', in_array('https', $w) ? 'yes':'no', PHP_EOL;
echo 'wrappers: ', var_dump($w);
?>

returns:

allow_url_fopen: yes
openssl: yes
http wrapper: yes
https wrapper: yes
wrappers: array(12) {
  [0]=>
  string(5) "https"
  [1]=>
  string(4) "ftps"
  [2]=>
  string(13) "compress.zlib"
  [3]=>
  string(14) "compress.bzip2"
  [4]=>
  string(3) "php"
  [5]=>
  string(4) "file"
  [6]=>
  string(4) "glob"
  [7]=>
  string(4) "data"
  [8]=>
  string(4) "http"
  [9]=>
  string(3) "ftp"
  [10]=>
  string(4) "phar"
  [11]=>
  string(3) "zip"
}

Update 7
cURL returns for https://www.example.com/foo.html this header:

string(138) "HTTP/1.1 301 Moved Permanently
Location: http://example.com/foo.html
Content-Length: 0
Content-Type: text/html; charset=UTF-8

"

Update 8
https://www.ssllabs.com/ssltest/analyze.html?d=example.com returns: enter image description here

Update 9
I tried the same script and URL through a foreign server and there it works. Of course it needs verify_peer set to false to avoid the ssl certificate error posted in Update 5. I checked phpinfo(). There are some differences. But both use SSL Version OpenSSL/1.0.1e and PHP Version 5.5.23 through CGI/FastCGI, but one setting on my server attracts my attention:

Registered Stream Socket Transports tcp, udp, unix, udg, ssl, sslv3, tls

The foreign server has in addition sslv2. Could that be the reason?

Update 10 - temporarily solved
After exchanging some emails with my hosting provider a technician wanted to play around with a new script (follow_location=false and verify_peer=false) and executed it through the console:

php -d allow_url_fopen=On ./t052.php |less

Then he emailed me that he had no problem. I tested it by myself through the browser and suprisingly his and all my other test scripts work again?! He confirmed me that he did not change anything. This is really frustrating as it looks like a random behaviour now. As this project is still running and collecting URLs I will update my question if it happens again.

Upvotes: 4

Views: 7434

Answers (2)

Common Man
Common Man

Reputation: 115

could you try this snippet and let me know your comments,

function send($url) {
$command = 'curl -k '.$url;
return exec($command, $output, $retValue);
}

Upvotes: 0

jmgross
jmgross

Reputation: 2336

Maybe you should disable the SSL verification in your cURL request :

curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, 0);
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, 0);

And set timeout too :

curl_setopt($ch, CURLOPT_CONNECTTIMEOUT ,30); 
curl_setopt($ch, CURLOPT_TIMEOUT, 400);

Update :

For file_get_contents, according to this answer, the php_openssl extension must be enabled and allow_url_fopen have to be active

Upvotes: 2

Related Questions