PHP Multiple cURL requests to REST API stalls - php

Currently I have a system that sends multiple requests to a REST API. It is structured something like this:
foreach ($data as $d)
{
$ch = curl_init( $url );
curl_setopt( $ch, CURLOPT_POST, 1);
curl_setopt( $ch, CURLOPT_HTTPHEADER, (array of data here));
curl_setopt( $ch, CURLOPT_RETURNTRANSFER, 1);
$response = curl_exec( $ch );
$retry = 0;
while((curl_errno($ch) == 7 || curl_errno($ch) == 52) && $retry < 3)
{
$response = curl_exec($ch);
$retry++;
}
curl_close($ch);
(decode XML Response and loop)
}
(I can't expose the whole code so I have filled in the operations that are happening in brackes)
However after a few hundred requests the FastCGI script stalls. The REST API will still respond during this period if I query it in another fashion, but this batch client will not send anymore requests. After a few minutes, it will start responding again. I'm not sure why this is stalling, I can see via htop that there is no CPU activity on the threads at either end whilst this is happening.
Is there any reason why the cURL/PHP script would stall here?

if you allowed to use external PHP libraries;
I'd like to suggest this method:
https://github.com/php-curl-class/php-curl-class
// Requests in parallel with callback functions.
$multi_curl = new MultiCurl();
$multi_curl->success(function($instance) {
echo 'call to "' . $instance->url . '" was successful.' . "\n";
echo 'response: ' . $instance->response . "\n";
});
$multi_curl->error(function($instance) {
echo 'call to "' . $instance->url . '" was unsuccessful.' . "\n";
echo 'error code: ' . $instance->error_code . "\n";
echo 'error message: ' . $instance->error_message . "\n";
});
$multi_curl->complete(function($instance) {
echo 'call completed' . "\n";
});
$multi_curl->addGet('https://www.google.com/search', array(
'q' => 'hello world',
));
$multi_curl->addGet('https://duckduckgo.com/', array(
'q' => 'hello world',
));
$multi_curl->addGet('https://www.bing.com/search', array(
'q' => 'hello world',
));
$multi_curl->start();

Related

Grab the race number from a tracking software

I want to grab this number (28/28) with PHP from a tracking software (https://aircrasher.livefpv.com/live/scoring/) for drone races. But I can't. I think it's a websocket, but I don't know how to retrieve information from it. I tried curl, I tried the "ultimate-web-scraper" from GitHub (https://github.com/cubiclesoft/ultimate-web-scraper). I tried a code example from another post from here (PHP simple web socket client) but I'm not able to get this number. Is it imposible to get this number (with php)?
This is what I tried so far with the help of "ultimate-web-scraper" (https://kleesit.de/beta/livetime/webscraper/test.php):
<?php
require_once "support/web_browser.php";
require_once "support/tag_filter.php";
// Retrieve the standard HTML parsing array for later use.
$htmloptions = TagFilter::GetHTMLOptions();
// Retrieve a URL (emulating Firefox by default).
$url = "https://aircrasher.livefpv.com/live/scoring/";
$web = new WebBrowser();
$result = $web->Process($url);
// Check for connectivity and response errors.
if (!$result["success"])
{
echo "Error retrieving URL. " . $result["error"] . "\n";
exit();
}
if ($result["response"]["code"] != 200)
{
echo "Error retrieving URL. Server returned: " . $result["response"]["code"] . " " . $result["response"]["meaning"] . "\n";
exit();
}
// Get the final URL after redirects.
$baseurl = $result["url"];
// Use TagFilter to parse the content.
$html = TagFilter::Explode($result["body"], $htmloptions);
// Find all anchor tags inside a div with a specific class.
// A useful CSS selector cheat sheet: https://gist.github.com/magicznyleszek/809a69dd05e1d5f12d01
echo "All the URLs:\n";
$result2 = $html->Find("div.someclass a[href]");
if (!$result2["success"])
{
echo "Error parsing/finding URLs. " . $result2["error"] . "\n";
exit();
}
foreach ($result2["ids"] as $id)
{
// Faster direct access.
echo "\t" . $html->nodes[$id]["attrs"]["href"] . "\n";
echo "\t" . HTTP::ConvertRelativeToAbsoluteURL($baseurl, $html->nodes[$id]["attrs"]["href"]) . "\n";
}
// Find all table rows that have 'th' tags.
// The 'tr' tag IDs are returned.
$result2 = $html->Filter($html->Find("tr"), "th");
if (!$result2["success"])
{
echo "Error parsing/finding table rows. " . $result2["error"] . "\n";
exit();
}
foreach ($result2["ids"] as $id)
{
echo "\t" . $html->GetOuterHTML($id) . "\n\n";
}
?>
This is what I tried with CURL and the help of a stackoverflow post (https://kleesit.de/beta/livetime/test2.php):
<?php
// random 7 chars digit/alphabet for "t" param
$random = substr(md5(mt_rand()), 0, 7);
$socket_server='https://aircrasher.livefpv.com/live/scoring/';
// create curl resource
$ch = curl_init();
//N°1 GET request
curl_setopt_array(
$ch,
[
CURLOPT_URL => $socket_server,
CURLOPT_RETURNTRANSFER => TRUE,
// since it is TRUE by default we should disable it
// on our localhost, but from real https server it will work with TRUE
CURLOPT_SSL_VERIFYPEER => FALSE
]);
// $output contains the output string
$output = curl_exec($ch);
$output=substr($output, 1);
echo $socket_server;
// server response in my case was looking like that:
// '{"sid":"4liJK2jWEwmTykwjAAAR","upgrades":["websocket"],"pingInterval":25000,"pingTimeout":20000,"maxPayload":1000000}'
var_dump($output);
$decod=json_decode($output);
// setting ping Interval accordingly with server response
$pingInterval = $decod->pingInterval;
//N°2 POST request
$socket_server_with_sid = $socket_server.'&sid='.$decod->sid;
curl_setopt_array(
$ch,
[
CURLOPT_URL => $socket_server_with_sid,
CURLOPT_POST => TRUE,
CURLOPT_TIMEOUT_MS => $pingInterval,
// 4 => Engine.IO "message" packet type
// 0 => Socket.IO "CONNECT" packet type
CURLOPT_POSTFIELDS => '40'
]);
$output = curl_exec($ch);
// ok
var_dump($output);
// Pervious 2 requests are called "hand shake" now we can send a message
// N°3 socket.emit
if ($output==='ok') {
curl_setopt_array(
$ch,
[
CURLOPT_URL => $socket_server_with_sid,
CURLOPT_TIMEOUT_MS => $pingInterval,
CURLOPT_POST => TRUE,
// 4 => Engine.IO "message" packet type
// 2 => Engine.IO "EVENT" packet type
CURLOPT_POSTFIELDS => '42["chat message","0devhost message 10"]'
]);
$output = curl_exec($ch);
// ok
echo $output.'<br/>';
echo $socket_server_with_sid;
}
// close curl resource to free up system resources
curl_close($ch);
?>

Discord oAuth2 API ratelimit triggered without any (known) cause

First question! Woho!
I'm currently coding a Dashboard for a Discord bot in PHP.
Recently, when trying to select a server, it would only display 3 malfunctioning items. So I checked the complete array using print_r() and it turned out it rate-limited me.
Now, I got no idea why that happened, as I'm the only person on the server. So what am I doing wrong? Here is the code for listing the selection page:
$all = DiscordAPI("/users/#me/guilds");
foreach ($all as $guild) {
echo "<a href='/?server=" . $guild['id'] . "'><p>";
if ($guild['icon']) {
if (substr($guild['icon'], 0, 2) == "a_") {
echo "<img class='server-icon' src='https://cdn.discordapp.com/icons/" . $guild['id'] . "/" . $guild['icon'] . ".gif?size=64'>";
} else {
echo "<img class='server-icon' src='https://cdn.discordapp.com/icons/" . $guild['id'] . "/" . $guild['icon'] . ".webp?size=64'>";
}
} else {
$words = explode(" ", $guild['name']);
$acronym = "";
foreach ($words as $w) {
$acronym .= $w[0];
}
echo "<img class='server-icon' src='https://cdn.statically.io/avatar/s=64/" . $acronym . "'>";
}
echo $guild['name'];
echo "</p></a>";
}
And here is the code for the DiscordAPI() function:
function DiscordAPI($endpoint)
{
if (!$_SESSION['token']) {
die("No token provided");
}
$ch = curl_init("https://discord.com/api" . $endpoint);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HEADER, 0);
curl_setopt($ch, CURLOPT_HTTPHEADER, array(
"Authorization: Bearer " . $_SESSION['token']
));
$data = curl_exec($ch);
curl_close($ch);
return json_decode($data, true);
}
There are 2 API calls before this code to verify that there is a valid token.
Quick sidenote: I got Autism, so I might understand stuff a bit differently. Don't be scared to ask if I worded something hard to understand. Thank you for trying to understand me.

cURL request in a loop sometimes returning nothing at all

The issue:
I'm working with PHP, cURL and a public API to fetch json strings.
These json strings are formatted like this (simplified, average size is around 50-60 kB):
{
"data": {},
"previous": "url",
"next": "url"
}
What am trying to do is fetch all the json strings starting from the first one by checking for the "next" attribute. So I have a while loop and as long as there's a "next" attribute, I fetch the next URL.
The problem is sometimes, randomly the loop stops before the end and I cannot figure out why after many tests.
I say randomly because sometimes the loop goes through to the end and no problem occurs. Sometimes it crashes after N loops.
And so far I couldn't extract any information to help me debug it.
I'm using PHP 7.3.0 and launching my code from the CLI.
What I tried so far:
Check the headers:
No headers are returned. Nothing at all.
Use curl_errno() and curl_error():
I tried the following code right after executing the request (curl_exec($ch)) but it never triggers.
if(curl_errno($ch)) {
echo 'curl error ' . curl_error($ch) . PHP_EOL;
echo 'response received from curl error :' . PHP_EOL;
var_dump($response); // the json string I should get from the server.
}
Check if the response came back null:
if(is_null($response))
or if my json string has an error:
if(!json_last_error() == JSON_ERROR_NONE)
Though I think it's useless because it will never be valid if the cURL response is null or empty. When this code triggers, the json error code is 3 (JSON_ERROR_CTRL_CHAR)
The problematic code:
function apiCall($url) {
...
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, $url);
curl_setopt($ch, CURLOPT_HTTPHEADER, $headers);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
$response = curl_exec($ch);
}
$inc = 0;
$url = 'https://api.example.com/' . $id;
$jsonString = apiCall($url);
if(!is_null($jsonString)) {
file_put_contents('pathToDirectory/' . $id + $inc, $jsonString);
$nextUrl = getNextUrl($jsonString);
while ($nextUrl) {
$jsonString = apiCall($url . '?page=' . $nextUrl);
if(!is_null($jsonString)) {
$inc++;
file_put_contents('pathToDirectory/' . $id + $inc, $jsonString);
$nextUrl = getNextUrl($jsonString);
}
}
}
What I'm expecting my code to do:
Not stop randomly, or at least give me a clear error code.
The problem is that your API could be returning an empty response, a malformed JSON, or even a status code different of 200 and you would stop execution imediately.
Since you do not control the API responses, you know that it can fail randomly, and you do not have access to the API server logs (because you don't, do you?); you need to build some kind of resilience in your consumer.
Something very simple (you'd need to tune it up) could be
function apiCall( $url, $attempts = 3 ) {
// ..., including setting "$headers"
$ch = curl_init();
curl_setopt( $ch, CURLOPT_URL, $url );
curl_setopt( $ch, CURLOPT_HTTPHEADER, $headers );
curl_setopt( $ch, CURLOPT_RETURNTRANSFER, true );
for ( $i = 0; $i < $attempts; $i ++ ) {
$response = curl_exec( $ch );
$curl_info = curl_getinfo( $ch );
if ( curl_errno( $ch ) ) {
// log your error & try again
continue;
}
// I'm only accepting 200 as a status code. Check with your API if there could be other posssible "good" responses
if ( $curl_info['http_code'] != 200 ) {
// log your error & try again
continue;
}
// everything seems fine, but the response is empty? not good.
if ( empty( $response ) ) {
// log your error & and try again
continue;
}
return $response;
}
return null;
}
This would allow you to do something like (pulled from your code):
do {
$jsonString = apiCall($url . '?page=' . $nextUrl);
$nextUrl = false;
if(!is_null($jsonString)) {
$inc++;
file_put_contents('pathToDirectory/' . $id + $inc, $jsonString);
$nextUrl = getNextUrl($jsonString);
}
}
while ($nextUrl);
I'm not checking if the return from the API is non-empty, not a connection error, a status different from '200' and yet an invalid JSON.
You may want to check for these things as well, depending on how brittle the API you are consuming is.

PHP curl_setopt : Variable Not working in place of URL string

I use CURL in php, and I use CURL something like this
$url = "http://exampledomain.com";
$smsURL = $url;
$curl = curl_init();
curl_setopt ($curl, CURLOPT_URL, $smsURL);
curl_exec ($curl);
curl_close ($curl);
This is not working, but if I wrote "http://exampledomain.com" in place of "$smsURL" at curl_setopt (); It will work fine. Where is issue in my code? did I miss something?
Original Code
$url = $this->conf['sms_getway_url'];
$url .= '&recipient=' . $_POST['txt_customer_contact_no'];
$url .= '&sender=' . strtoupper($saloon_info['saloon_name']);
$url .= '&is_payor=' . $this->conf['sms_is_payor'];
$url .= '&pay_amount=' . $this->conf['sms_pay_amount'];
$url .= '&token=5ce7467e9ec045cbbac448ba5a422a02';
//$url .= '&customer_num=' . $this->conf['sms_customer_num'] . $saloon_id;
$url .= '&customer_num=' . $this->conf['sms_customer_num'];
$appointment_time = date('H:i', strtotime($app_start_time));
$employee_name = $_POST['hdn_selected_employee_name']; //$value['id_employee'];
//$sms_msg = "Hey. Recalling that I await tomorrow at. " . $appointment_time . " Regards " . $employee_name . ", " . $saloon_name . ". ";
$sms_msg = t('msg_sms_book_appointment', array('%emp_name' => $employee_name, '%saloon_name' => $_POST['hdn_selected_saloon_name'], '%time' => $appointment_time));
$url .= '&sms_msg=' . $sms_msg;
$smsURL = $url;
$curl = curl_init();
curl_setopt ($curl, CURLOPT_URL, $smsURL);
curl_exec ($curl);
curl_close ($curl);
Thanks
You compose the URL from pieces but you don't encode the values properly. There are characters that have special meaning in URLs (/, ?, &, =, %, , + and a few more). They have to be encoded when they appear in the values from the query string, in order to retain their literal meaning.
PHP helps you for this goal with function urlencode() that can be used to encode each value individually when you create a query string. Something like this:
$url = $this->conf['sms_getway_url'];
$url .= '&recipient=' . urlencode($_POST['txt_customer_contact_no']);
$url .= '&sender=' . urlencode(strtoupper($saloon_info['saloon_name']));
...
But, because this is a tedious work, it also provides an easier method. Put all the values you need into an array, using the names of the variables as keys, then pass the array to function http_build_query(). There is no need to call urlencode() any more; http_build_query() takes care of it. Also it puts ampersands (&) between the variables and equals (=) where they belong.
The code is like this:
$url = $this->conf['sms_getway_url'];
// Prepare the values to put into the query string
$vars = array();
$vars['recipient'] = $_POST['txt_customer_contact_no'];
$vars['sender'] = strtoupper($saloon_info['saloon_name']);
$vars['is_payor'] = $this->conf['sms_is_payor'];
$vars['pay_amount'] = $this->conf['sms_pay_amount'];
$vars['token'] = '5ce7467e9ec045cbbac448ba5a422a02';
$vars['customer_num'] = $this->conf['sms_customer_num'];
$appointment_time = date('H:i', strtotime($app_start_time));
$employee_name = $_POST['hdn_selected_employee_name'];
$sms_msg = t('msg_sms_book_appointment', array(
'%emp_name' => $employee_name,
'%saloon_name' => $_POST['hdn_selected_saloon_name'],
'%time' => $appointment_time,
));
$vars['sms_msg'] = $sms_msg;
// Now, the magic comes into place
$smsURL = $url.'?'.http_build_query($vars);
$curl = curl_init();
curl_setopt ($curl, CURLOPT_URL, $smsURL);
if (! curl_exec ($curl)) {
// Something went wrong. Check the status code (at least)
$code = curl_getinfo($curl, CURLINFO_HTTP_CODE);
// Do something here.
// If $code >= 500 then the remote server encountered an internal error
// retry later or ask them to fix it
// If 400 <= $code < 500 then there is a problem with the request:
// maybe the resource is not there (404, 410)
// or you are not allowed to access it (403)
// or something else.
echo('Failure sending the SMS. HTTP status code is '.$code."\n");
}
curl_close ($curl);
Check the list of HTTP status codes for more details.

Unable to Download Remote File from server after using sleep in php

Hi there i am having a strange problem with downloading a remote file. I am using Flurry Data API to download some Reports. Thing is when first time we call the Flurry API it gives us XML/JSON response which contains the path of the Report for Download. It takes like 2 minutes for the report to get ready. I am having Problem with that thing. I wrote a Script which download the remote file if i just paste the link of Report directly to function which is already ready to download. It works like a charm but i have to automate the process of Downloading. So for that i First call the API and get the Report Download Link then i use sleep() function of PHP to stop execution for like 3 Minutes(Tried it with 2 also). Then i call the same function which i uses to download the report successfully doesn't work this time. Here is the File Download Method:
function get_file_and_save($file, $local_path, $newfilename) {
$err_msg = '';
$out = fopen($local_path . $newfilename . ".gz", 'wb');
if ($out == FALSE) {
print "File is not available<br>";
exit;
}
$ch = curl_init();
$headers = array('Content-type: application/x-gzip', 'Connection: Close');
curl_setopt($ch, CURLOPT_FILE, $out);
curl_setopt($ch, CURLOPT_HTTPHEADER, $headers);
curl_setopt($ch, CURLOPT_URL, $file);
curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, 0);
curl_exec($ch);
echo "<br>Error Occured:" . curl_error($ch);
curl_close($ch);
}
i also have tried giving the CURLOPT_TIMEOUT but it wasn't working either.
The code to Request to the Flurry note that download_path is working properly it just get the report link:
$query_url = 'http://api.flurry.com/rawData/Events?apiAccessCode=' . $ACCESS_CODE . '&apiKey=' . $API_KEY . '&startTime=' . $start_time . '&endTime=' . $end_time;
$response = download_path($query_url);
if ($response) {
$response_obj = json_decode($response, true);
if (isset($response_obj['report']['#reportUri'])) {
$report_link = $response_obj['report']['#reportUri'];
}
if (isset($response_obj['report']['#reportId'])) {
$report_id = $response_obj['report']['#reportId'];
}
if(isset($report_link) && !empty($report_link)){
echo "<br >Report Link: ".$report_link."<br >";
sleep(240);
$config = array(
'http' => array(
'header' => 'Accept: application/json',
'method' => 'GET'
)
);
$stream = stream_context_create($config);
$json= file_get_contents($report_link,false,$stream);
echo "<pre>";
print_r($http_response_header);
echo "</pre>";
if($http_response_header[3] == "Content-Type: application/octet-stream"){
get_file_and_save($report_link, "data-files/gz/", $current_time);
}
}
else{
echo "There was some error in downloading report";
}
} else {
$error = true;
echo "There was some error in genrating report";
}
is there something problem with sleep() or what i am stuck its been 2nd night i am unable to achieve it.
Check if your PHP script is timing out and being killed off. Both the webserver and PHP have max execution limits to prevent runaway scripts, and if your sleep surpasses that limit, it'll never continue beyond that.
http://www.php.net/manual/en/info.configuration.php#ini.max-execution-time
http://php-fpm.org/wiki/Configuration_File - request_terminate_timeout
http://nginx.org/en/docs/http/ngx_http_fastcgi_module.html#fastcgi_read_timeout
http://httpd.apache.org/docs/2.0/mod/core.html#timeout

Categories