Im trying to open an api and extract some data and about 1/5 times the script runs it errors with a
PHP Warning: file_get_contents(http://192.168.1.52/home.cgi): failed to open stream: HTTP request failed! 1
I would like to retry opening the api on error and then follow through with the rest of the code
This is running on a Pi using PHP5
$inverterDataURL = "http://".$dataManagerIP."/home.cgi";
$context = stream_context_create(array('http'=>array('protocol_version'=>'1.1')));
$result = file_get_contents('http://192.168.1.11/home.cgi', false, $context);
20% of the time when running the script it errors trying to open the api and without the api being open i can't grab any data out of it.
the rest of the script runs through fine when it opens correctly
You should use a loop for the retry logic that breaks when the result succeeds. A do ... while loop is used here because it guarantees that it'll be run at least once (therefore guaranteeing that $result will be set to something). When the file_get_contents fails, $result will be false:
<?php
$context = stream_context_create(array('http'=>array('protocol_version'=>'1.1')));
do {
$result = file_get_contents('http://127.0.0.1/home.cgi', false, $context);
if (!$result) {
echo "Waiting 3 seconds.\n";
sleep(3);
}
} while( !$result);
If the server goes down, you'll probably want something to break the loop after a few tries. This bit will stop trying after 5 failures.
<?php
$context = stream_context_create(array('http'=>array('protocol_version'=>'1.1')));
$attempts = 0;
do {
$attempts++;
echo "Attempt $attempts\n";
$result = file_get_contents('http://127.0.0.1/home.cgi', false, $context);
if (!$result) {
echo "Attempt $attempts has failed. Waiting 3 seconds.\n";
sleep(3);
}
} while( !$result && $attempts < 5);
Related
Hello I hosted my website into a free hosting site (5gbfree.com) and I created here a function where it retrieves the Peso-dollar rate based from the site http://ppa.com.ph??q=fcer_view where it simply select the a.active element and get the value as plaintext. It was working yesterday but when I checked it our again this morning it failed to fetch. So it will return the default value which is 50.
error_log:
[09-Apr-2017 13:49:32 Asia/Manila] PHP Warning: file_get_contents(http://www.ppa.com.ph/?q=fcer_view): failed to open stream: Connection timed out in /home/rasibaseport/public_html/simple_html_dom2.php on line 75
I am using simple html DOM.
Here's the function.
include "simple_html_dom2.php";
function PPA_peso_dollar_rate(){
// Create DOM from URL or file
error_reporting(E_ALL);
ini_set("display_errors", 0);
ini_set('default_socket_timeout', 15);
$html = file_get_html("http://www.ppa.com.ph/?q=fcer_view");
$ret = 0;
if($html === false){
$ret = 50;
}else {
foreach($html->find('a[class=active]') as $e)
$ret = $e->plaintext;
$explode = explode(" ", $ret);
$ret = 50;
foreach($explode as $ex){
if(is_numeric($ex)){
$ret = $ex;
}
}
if($ret == 0) $ret = 50;
}
echo $ret;
}
Unfortunately 5gbfree disabled curl_init() function.
curl_init() has been disabled for security reasons in /home/rasibaseport/public_html/config.php on line 38
Is there any work a round here? I appreciate any help. Thank you very much.
Edit: I forgot to mention that testing this with localhost(xampp), the expected return value is correct with no errors and warnings. Works perfectly fine.
UPDATE: After trying #Rafiq's updated solution, nothing worked. It gave me similar error.
[09-Apr-2017 20:18:53 Asia/Manila] PHP Warning: file_get_contents(http://www.ppa.com.ph/?q=fcer_view): failed to open stream: Connection timed out in /home/rasibaseport/public_html/simple_html_dom2.php on line 46
Your code is work for me. Its problem in execution time. Add the following code to increase maximum execution time.
ini_set('max_execution_time', 300); //300 seconds = 5 minutes
ini_set('default_socket_timeout', 100); // 100 seconds = 1 Minutes 40 sec
//call the function file_get_html();
Explanation of parameter max_execution_time inside ini_set finction
This sets the maximum time in seconds a script is allowed to run before it is terminated by the parser. This helps prevent poorly written scripts from tying up the server. The default setting is 30. For detail read Runtime Configuration
To get rid of the following two error use fetch_http_file_contents($url) instead of file_get_contents($url) inside simple_html_dom.php on line 75.
file_get_contents(): failed to open stream: No route to host
file_get_contents(): failed to open stream: Connection timed out
function fetch_http_file_contents($url) {
$hostname = parse_url($url, PHP_URL_HOST);
if ($hostname == FALSE) {
return FALSE;
}
$host_has_ipv6 = FALSE;
$host_has_ipv4 = FALSE;
$file_response = FALSE;
$dns_records = dns_get_record($hostname, DNS_AAAA + DNS_A);
foreach ($dns_records as $dns_record) {
if (isset($dns_record['type'])) {
switch ($dns_record['type']) {
case 'AAAA':
$host_has_ipv6 = TRUE;
break;
case 'A':
$host_has_ipv4 = TRUE;
break;
} } }
if ($host_has_ipv6 === TRUE) {
$file_response = file_get_intbound_contents($url, '[0]:0');
}
if ($host_has_ipv4 === TRUE && $file_response == FALSE) {
$file_response = file_get_intbound_contents($url, '0:0');
}
return $file_response;
}
function file_get_intbound_contents($url, $bindto_addr_family) {
$stream_context = stream_context_create(
array(
'socket' => array(
'bindto' => $bindto_addr_family
),
'http' => array(
'timeout'=>20,
'method'=>'GET'
) ) );
return file_get_contents($url, FALSE, $stream_context);
}
Source Making file_get_contents() more routing-robust and dual-stack
Hi I'm trying server sent events(SSE) using php, I have a https url where I get the live streaming data. Below is my script where I'm trying in infinite loop.
PHP:
<?php
while(1)
{
$get_stream_data = fopen('https://api.xyz.com:8100/update-stream/connect', 'r');
if($get_stream_data)
{
$stream_data = stream_get_contents($get_stream_data);
$save_stream_data = getStreamingData($stream_data);
if($save_stream_data == true)
{
continue;
}
}
else
{
sleep(1);
continue;
}
}
function getStreamingData($stream_data)
{
$to = "accd#xyz.com";
$subject = "Stream Details";
$msg = "Stream Details : ".$stream_data;
$headers = "From:streamdetail#xyz.com";
$mailsent = mail($to,$subject,$msg,$headers);
if($mailsent){
return true;
}else {
return false;
}
}
?>
Error:
Warning: fopen(https://api.xyz.com:8100/update-stream/connect): failed to open stream: Connection timed out in /home/public_html/get_stream_data/index.php on line 4
I couldn't get the data by my end while it is giving an updates by the server in live.
I checked that live streaming in a command prompt using below command.
CURL
curl --get 'https://api.xyz.com:8100/update-stream/connect' --verbose
First, this is best done with PHP's curl functions. See the various answers to PHP file_get_contents() returns "failed to open stream: HTTP request failed!"
If you stick with fopen() you probably need to set up the context for SSL, and it may involve installing some certificates. See file_get_contents(): SSL operation failed with code 1. And more (and note the security warning about the accepted answer)
Finally, your while(1) loop is around the fopen() (which is okay for re-starts after relatively rare failures), but you actually want it inside. Here is your code with just the minimal changes to show that:
<?php
while(1)
{
$get_stream_data = fopen('https://api.xyz.com:8100/update-stream/connect', 'r');
if($get_stream_data)while(1)
{
$stream_data = stream_get_contents($get_stream_data);
$save_stream_data = getStreamingData($stream_data);
if($save_stream_data == true)
{
continue;
}
sleep(1);
}
else
{
sleep(1);
continue;
}
}
UPDATE: The above code still nags at me: I think you want me to using fread() instead of stream_get_contents(), and use blocking instead of the sleep(1) (in the inner loop).
BTW, I'd suggest changing the outer-loop sleep(1) to be sleep(3) or sleep(5) which are typical defaults in Chrome/Firefox/etc. (Really, you should be looking for the SSE server sending a "retry" header, and using that number as the sleep.)
I'm not an expert with PHP. I have a function which uses EXEC to run WINRS whcih then runs commands on remote servers. The problem is this function is placed into a loop which calls getservicestatus function dozens of times. Sometimes the WINRS command can get stuck or take longer than expected causing the PHP script to time out and throw a 500 error.
Temporarily I've lowered the set timeout value in PHP and created a custom 500 page in IIS and if the referring page is equal to the script name then reload the page (else, throw an error). But this is messy. And obviously it doesn't apply to each time the function is called as it's global. So it only avoids the page stopping at the HTTP 500 error.
What I'd really like to do is set a timeout of 5 seconds on the function itself. I've been searching quite a bit and have been unable to find an answer, even on stackoverflow. Yes, there are similar questions but I have not been able to find any that relate to my function. Perhaps there's a way to do this when executing the command such as an alternative to exec()? I don't know. Ideally I'd like the function to timeout after 5 seconds and return $servicestate as 0.
Code is commented to explain my spaghetti mess. And I'm sorry you have to see it...
function getservicestatus($servername, $servicename, $username, $password)
{
//define start so that if an invalid result is reached the function can be restarted using goto.
start:
//Define command to use to get service status.
$command = 'winrs /r:' . $servername . ' /u:' . $username . ' /p:' . $password . ' sc query ' . $servicename . ' 2>&1';
exec($command, $output);
//Defines the server status as $servicestate which is stored in the fourth part of the command array.
//Then the string "STATE" and any number is stripped from $servicestate. This will leave only the status of the service (e.g. RUNNING or STOPPED).
$servicestate = $output[3];
$strremove = array('/STATE/','/:/','/[0-9]+/','/\s+/');
$servicestate = preg_replace($strremove, '', $servicestate);
//Define an invalid output. Sometimes the array is invalid. Catch this issue and restart the function for valid output.
//Typically this can be caught when the string "SERVICE_NAME" is found in $output[3].
$badservicestate = "SERVICE_NAME" . $servicename;
if($servicestate == $badservicestate) {
goto start;
}
//Service status (e.g. Running, Stopped Disabled) is returned as $servicestate.
return $servicestate;
}
The most straightforward solution, since you are calling an external process, and you actually need its output in your script, is to rewrite exec in terms of proc_open and non-blocking I/O:
function exec_timeout($cmd, $timeout, &$output = '') {
$fdSpec = [
0 => ['file', '/dev/null', 'r'], //nothing to send to child process
1 => ['pipe', 'w'], //child process's stdout
2 => ['file', '/dev/null', 'a'], //don't care about child process stderr
];
$pipes = [];
$proc = proc_open($cmd, $fdSpec, $pipes);
stream_set_blocking($pipes[1], false);
$stop = time() + $timeout;
while(1) {
$in = [$pipes[1]];
$out = [];
$err = [];
stream_select($in, $out, $err, min(1, $stop - time()));
if($in) {
while(!feof($in[0])) {
$output .= stream_get_contents($in[0]);
break;
}
if(feof($in[0])) {
break;
}
} else if($stop <= time()) {
break;
}
}
fclose($pipes[1]); //close process's stdout, since we're done with it
$status = proc_get_status($proc);
if($status['running']) {
proc_terminate($proc); //terminate, since close will block until the process exits itself
return -1;
} else {
proc_close($proc);
return $status['exitcode'];
}
}
$returnValue = exec_timeout('YOUR COMMAND HERE', $timeout, $output);
This code:
uses proc_open to open a child process. We only specify the pipe for the child's stdout, since we have nothing to send to it, and don't care about its stderr output. if you do, you'll have to adjust the following code accordingly.
Loops on stream_select(), which will block for a period up to the $timeout set ($stop - time()).
If there is input, it will var_dump() the contents of the input buffer. This won't block, because we have stream_set_blocking($pipe[1], false) on the pipe. You will likely want to save the content into a variable (appending it rather than overwriting it), rather than printing out.
When we have read the entire file, or we have exceeded our timeout, stop.
Cleanup by closing the process we have opened.
Output is stored in the pass-by-reference string $output. The process's exit code is returned, or -1 in the case of a timeout.
I am executing commands(fputs) on socket/telnet console and getting output/result(fread) by below code and it's working perfectly fine.
//open socket let's say ip = 192.168.10.5 and port = 21
$this->socketResource = fsockopen($this->nodeIp,$this->portNumber);
//execute some commands, for example "ipconfig"
fputs($this->socketResource,$command);
//get output string
$output = fread($this->socketResource,30000);
Now my requirement is to get all console/socket output without executing any command by fputs. For example, Cisco routers give continuous debug messages/prints on the telnet console/socket without executing any command by fputs.
How can i capture(fread) any telnet session output continuously for some duration without executing any command(fputs)?
If i capture in discrete fashion like every x seconds, i will definitely miss some console output.
For this, I would would switch over to the stream_* family. There is a huge improvement when trying to accomplish the above with performance and extending.
$stream = stream_socket_client("tcp://10.1.1.1:23", $errno, $errstr, 30);
if (!$stream ) {
echo "$errstr ($errno)<br />\n";
} else {
fwrite($stream , "sh run" . PHP_EOL);
// Set Blocking Mode - Wait For A Response On The Stream
stream_set_blocking( $stream , true );
while( true ){
// This is your response
echo stream_get_contents( $stream );
}
}
You will need to add something above to break the while(true) loop, or the script will run forever, but this is an approach I use to do something similar.
Here is the code that I am using:
if (!($fp = fsockopen('ssl://imap.gmail.com', '993', $errno, $errstr, 15)))
echo "Could not connect to host";
$server_response = fread($fp, 256);
echo $server_response;
fwrite($fp, "C01 CAPABILITY"."\r\n");
while (!feof($fp)) {
echo fgets($fp, 256);
}
I get the first response:
OK Gimap ready for requests from xx.xx.xx.xx v3if9968808ibd.15
but then the page times out. I have searched through stream_set_blocking, stream_set_timeout, stream_select, fread, etc. but could not get it to work. I need to read all the data that the server sends and then proceed with other commands (I would be retrieving emails using imap).
Thanks
Your script is hanging in the while loop at the end. This is because you have used !feof() as the condition for the loop, and the server is not closing the connection. This means the feof() will always return false and the loop will continue forever.
This will not be problem when your write a full implementation, as you will be looking for response codes and can break out of the loop accordingly, for example:
<?php
// Open a socket
if (!($fp = fsockopen('ssl://imap.gmail.com', 993, $errno, $errstr, 15))) {
die("Could not connect to host");
}
// Set timout to 1 second
if (!stream_set_timeout($fp, 1)) die("Could not set timeout");
// Fetch first line of response and echo it
echo fgets($fp);
// Send data to server
echo "Writing data...";
fwrite($fp, "C01 CAPABILITY\r\n");
echo " Done\r\n";
// Keep fetching lines until response code is correct
while ($line = fgets($fp)) {
echo $line;
$line = preg_split('/\s+/', $line, 0, PREG_SPLIT_NO_EMPTY);
$code = $line[0];
if (strtoupper($code) == 'C01') {
break;
}
}
echo "I've finished!";
Your script should be working. In fact, it is working.
See the results below on my pc when I ran your code:
* OK Gimap ready for requests from xx.xx.xx.xx l5if4585958ebb.20
* CAPABILITY IMAP4rev1 UNSELECT IDLE NAMESPACE QUOTA ID XLIST CHILDREN X-GM-EXT-1 XYZZY SASL-IR AUTH=XOAUTH
C01 OK Thats all she wrote! l5if4585958ebb.20
Since gmail doesn't disconnect you. No end of file occurs. And the page loading simply times out.
In other words: Your script will just keep waiting and waiting until gmail does disconnect, which unfortunately happens after your page load has already timed out.