I am working on a PHP script that makes an API call to a external site. However, if this site is not available or the request times out, I would like my function to return false.
I have found following, but I am not sure on how to implement it on my script, since i use "file_get_contents" to retrieve the content of the external file call.
Limit execution time of an function or command PHP
$fp = fsockopen("www.example.com", 80);
if (!$fp) {
echo "Unable to open\n";
} else {
fwrite($fp, "GET / HTTP/1.0\r\n\r\n");
stream_set_timeout($fp, 2);
$res = fread($fp, 2000);
$info = stream_get_meta_data($fp);
fclose($fp);
if ($info['timed_out']) {
echo 'Connection timed out!';
} else {
echo $res;
}
}
(From: http://php.net/manual/en/function.stream-set-timeout.php)
How would you adress such an issue? Thanks!
I'd recommend using the cURL family of PHP functions. You can then set the timeout using curl_setopt():
curl_setopt($ch,CURLOPT_CONNECTTIMEOUT,2); // two second timeout
This will cause the curl_exec() function to return FALSE after the timeout.
In general, using cURL is better than any of the file reading functions; it's more dependable, has more options and is not regarded as a security threat. Many sysadmins disable remote file reading, so using cURL will make your code more portable and secure.
<?php
$fp = fsockopen("www.example.com", 80);
if (!$fp) {
echo "Unable to open\n";
} else {
stream_set_timeout($fp, 2); // STREAM RESOURCE, NUMBER OF SECONDS TILL TIMEOUT
// GET YOUR FILE CONTENTS
}
?>
From the PHP manual for File_Get_Contents (comments):
<?php
$ctx = stream_context_create(array(
'http' => array(
'timeout' => 1
)
)
);
file_get_contents("http://example.com/", 0, $ctx);
?>
<?php
$fp = fsockopen("www.example.com", 80, $errno, $errstr, 4);
if ($fp) {
stream_set_timeout($fp, 2);
}
Related
I connect to the whois server and am able to retrieve the availability of a domain name.
Somehow I am not able to get a response back using the same connection when I do a request of a different domain name.
<?php
$context = stream_context_create();
if($fp = stream_socket_client("tcp://whois.eu:43", $errno, $errstr, 30, STREAM_CLIENT_CONNECT | STREAM_CLIENT_PERSISTENT, $context)) {
stream_set_timeout($fp, 30);
$domains = array('test.eu','amaai.eu');
foreach($domains as $domain) {
fwrite($fp, $domain."\r\n");
$contents = '';
while (!feof($fp)) {
$contents .= fread($fp, 8192);
}
echo $domain.": ".$contents;
}
fclose($fp);
}
What am I missing?
I really want to use the same connection.
The WHOIS protocol only supports one query. The server closes the connection after sending a response. You need to reconnect for each query.
I think I came down to a very specific PHP bug, that the timeouts set by stream_set_timeout() doesn't get obeyed, ONLY if using SSL websockets.
I am able to reproduce it with the following simple script:
<?php
$context = stream_context_create();
$fp = stream_socket_client ("ssl://echo.websocket.org:443",$errno,$errstr,5,STREAM_CLIENT_CONNECT,$context);
stream_set_timeout($fp, 5);
if (!$fp) {
echo "Unable to open\n";
} else {
//fwrite($fp, "GET / HTTP/1.0\r\n\r\n");
stream_set_timeout($fp, 5);
$res = fread($fp, 2000);
$info = stream_get_meta_data($fp);
//fclose($fp);
if ($info['timed_out']) {
echo 'Connection timed out!';
} else {
echo $res;
}
}
If you switch out ssl://echo.websocket.org:443 with tcp://echo.websocket.org:80, the timeout gets triggered correctly after 5 seconds.
Is this a PHP bug or am I missing some sort of setting?
I have the following (stripped-down) piece of code:
function curl_request_async($url, $params)
{
foreach ($params as $key => $val) {
$post_params[] = $key.'='.urlencode($val);
}
$post_string = implode('&', $post_params);
$parts=parse_url($url);
$fp = fsockopen($parts['host'],
isset($parts['port'])?$parts['port']:80,
$errno, $errstr, 30);
fwrite($fp, "$type ".$parts['path']." HTTP/1.1\r\n");
fwrite($fp, "Host: ".$parts['host']."\r\n");
fwrite($fp, "Content-Type: application/x-www-form-urlencoded\r\n");
fwrite($fp, "Content-Length: ".strlen($post_string)."\r\n");
fwrite($fp, "Connection: Close\r\n\r\n");
$bytes_written = fwrite($fp, $post_string);
var_dump($bytes_written, strlen($post_string));
// fread($fp, 1);
// fflush($fp);
fclose($fp);
}
The problem with this code is that I found no evidence the request reached the server called. The line var_dump($bytes_written, strlen($post_string)); outputted int(493) int(493), so it should have received all data, yet it didn't.
If I uncomment fread($fp, 1); it works without a problem. That could be working solution, but it doesn't seem to make sense. There has to be a better way!
My question then is two-fold: why does fread($fp, 1); fix my problem and is there a better solution?
your problem is probably that you wrote the server code in PHP, and you dont have ignore_user_abort=true by default (see http://php.net/manual/en/misc.configuration.php#ini.ignore-user-abort ), so when you close the connection, your server stop executing your php code, thus fread(fp,1) fix your problem - connection dont close before php start writing a response
you can use this code to make a server to test if its actually connecting or not -
<?php
error_reporting(E_ALL);
ini_set('display_errors',1);
$sck=socket_create(AF_INET,SOCK_STREAM,SOL_TCP);
if($sck===FALSE){
die('socket_create failed!');
}
if(!socket_set_block($sck)){
die("socket_set_block failed!");
}
if(!socket_bind($sck, '0.0.0.0',1337)){
die("FAILED to bind to port 1337");
}
if(!socket_listen($sck,0)){
die("socket_listen failed!");
}
$fullFile='';
while((print('listening for connections!'.PHP_EOL)) && false!==($conn=socket_accept($sck))){
echo "new connection!".PHP_EOL;
echo "generating crypto iv..";
while(false!==($buffi=socket_recv($conn,$buff,1024,MSG_WAITALL))){
if($buffi===0){
break;//socket_recv's way of
//saying that the connection closed,
//apparently. the docs say it should return
// false, but it doesn't, it just infinitely returns int(0).
// at least on windows 7 x64 sp1.
}
$fullFile.=$buff;
echo "recieved ".strlen($fullFile)." bytes...".PHP_EOL;
$buff='';//do i need to clear it? or wiill recv do it for me?
}
echo "all bytes recieved (i guess, todo, socket_last_error confirm).";
echo PHP_EOL;var_dump($fullFule);
echo "done!".PHP_EOL;
}
die("should never reach this code...");
it will make a netcat-style server on http://127.0.0.1:1337
fread needs two parameters: a resource and a length number of bytes to read.
Right now you are only reading 1 byte. fread($fp, 1);
If you want to read the complete result, loop it until readed completely:
while(!feof($fp)){
echo fread($fp, 128);
}
I'm using some sites to detect my site visitor's country. I mean like this
$ip = $_SERVER['REMOTE_ADDR'];
$url1 = 'http://api.hostip.info/get_json.php?ip='.$ip;
$url2 = 'http://ip2country.sourceforge.net/ip2c.php?format=JSON&ip='.$ip;
Sometimes sites like sourgeforge taking too much time to load.
So can anyone tell how to limit the http response time.?
if url1 is down or not responded in x seconds then move to url2,url3,etc
$context = stream_context_create(array(
'http' => array(
'method' => 'GET',
, 'timeout' => 3
)
));
Then supply the stream context to fopen() or file_get_contents() etc...
http://php.net/manual/en/stream.contexts.php
http://php.net/manual/en/context.http.php
The manual calls that a "read timeout". I worry it may not include time for stuff like dns resolution + socket connection. I think the timeout before php tries reading from the stream may be governed by the default_socket_timeout setting.
You may want to consider curl, it seems a bit more specific, but I'm not sure if CURLOPT_TIMEOUT is inclusive of CURLOPT_CONNECTTIMEOUT.
$ch = curl_init();
curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, 2);
curl_setopt($ch, CURLOPT_TIMEOUT, 2);
http://php.net/manual/en/function.curl-setopt.php
If this is done using streams, you could use stream_set_timeout for this. A decent example from the php manual, it also describes more advanced ways of archieving this:
$fp = fsockopen("www.example.com", 80);
if (!$fp) {
echo "Unable to open\n";
} else {
fwrite($fp, "GET / HTTP/1.0\r\n\r\n");
stream_set_timeout($fp, 2);
$res = fread($fp, 2000);
$info = stream_get_meta_data($fp);
fclose($fp);
if ($info['timed_out']) {
echo 'Connection timed out!';
} else {
echo $res;
}
}
There is another solution, just download the DB and offer that service to yourself on a faster machine of your own:
IP to Geolocation db
I'm using PHPMailer and it uses fsockopen to access the SMTP server.
But the machine has two IPs with different reverse DNS records. So in email headers I got the following:
Received: from one-server.tld (HELO another-server.tld) ...
I need to hide one-server.tld in favor of another-server.tld. But I need both IPs with their current RDNS settings.
I think its not possible using fsockopen. But its possible in curl, fopen and stream functions. What you need is stream_socket_client() function.
Here are some ways to achieve it.
Using context parameters which can be used in fopen function family and stream function family. See the example.
$opts = array(
'socket' => array(
'bindto' => '192.168.0.100:0',
),
);
// create the context...
$context = stream_context_create($opts);
$contents = fopen('http://www.example.com', 'r', false, $context);
Also stream_socket_client
$fp = stream_socket_client("tcp://www.example.com:80", $errno, $errstr, 30, STREAM_CLIENT_CONNECT, $opts);
if (!$fp) {
echo "$errstr ($errno)<br />\n";
} else {
fwrite($fp, "GET / HTTP/1.0\r\nHost: www.example.com\r\nAccept: */*\r\n\r\n");
while (!feof($fp)) {
echo fgets($fp, 1024);
}
fclose($fp);
}
Using socket_bind. PHP.NET got a simple example here.