php - Detect bad request - php

I have 2 JSON sources and one of them reply 400 Bad request (depend of charge in servers)
So I want that my php code check the answer of both server and select working one
<?php
$server1 = 'server1.lan'
$server2 = 'server2.lan'
/*
Here a code to check and select the working server
*/
$json=file_get_contents('https://'.$workingServer.'/v1/data?source='.$_GET['source']);
$data = json_decode($json);
if (count($data->data)) {
// Cycle through the array
foreach ($data->data as $idx => $data) {
echo "<p>$data->name</p>\n";
?>
Thanks !

Below is an idea of what you may want to implement. Your goal is to get that idea and implement something like that in your own way, with a normal error handling and removal of code duplication:
$json = file_get_contents('https://server1.lan/v1/data');
if ($json === false)
{
$json = file_get_contents('https://server2.lan/v1/data');
if ($json === false)
{
die('Both servers are unavailable');
}
}
file_get_contents returns boolean false on failure, so if the first server is unavailable, call the second. If it is also unavailable, exit the script, or do some sort of error handling that you prefer.
You may want to create an array of possible server names, and use a function that iterates over all of them until it finds a working one, and returns the contents, or throws an exception on failure.
I would also suggest that you use curl, which gives you an option to see the error codes of the request, customize the request itself, and so on.

Check $http_response_header after making the file_get_contents call.
$json = file_get_contents(('https://'.$server1.'/v1/data?source='.$_GET['source']);
if (strpos($http_response_header[0],"400") > 0)
{
$json = file_get_contents(('https://'.$server.'/v1/data?source='.$_GET['source']);
}
See examples at http://php.net/manual/en/reserved.variables.httpresponseheader.php

Related

Guzzle / Laravel cURL error 6: Could not resolve host: api.coingecko.com [duplicate]

This question already has answers here:
curl: (6) Could not resolve host: google.com; Name or service not known
(7 answers)
Closed 9 months ago.
Ok so I am a little stuck with this issue. I have a foreach loop (usually 50 results) that queries an API using Guzzle via Laravel Http and I am getting really inconsistent results.
I monitor the inserts in the database as they come in and sometimes the process seems slow and other times the process will fail with the following after x number of returned results.
cURL error 6: Could not resolve host: api.coingecko.com
The following is the actual code im using to fetch the results.
foreach ($json_result as $account) {
var_dump($account['name']);
$name = $account['name'];
$coingecko_id = $account['id'];
$identifier = strtoupper($account['symbol']);
$response_2 = Http::get('https://api.coingecko.com/api/v3/coins/'.urlencode($coingecko_id).'?localization=false');
if($response_2->successful()){
$json_result_extra_details = $response_2->json();
if( isset($json_result_extra_details['description']['en']) ){
$description = $json_result_extra_details['description']['en'];
}
if( isset($json_result_extra_details['links']['twitter_screen_name']) ){
$twitter_screen_name = $json_result_extra_details['links']['twitter_screen_name'];
}
}else {
// Throw an exception if a client or server error occurred...
$response_2->throw();
}
$crypto_account = CryptoAccount::updateOrCreate(
[
'identifier' => $identifier
],
[
'name' => $name,
'identifier' => $identifier,
'type' => "cryptocurrency",
'coingecko_id' => $coingecko_id,
'description' => $description,
]);
//sleep(1);
}
Now I know I am within the API rate limit of 100 calls a minute so I don't think that is the issue. I am wondering if this is a server/api issue which I don't really have any control over or if it related to my code and how Guzzle is implemented.
When I do single queries I don't seem to have a problem, the issue seems to be when it is inside the foreach loop.
Any advice would be great. Thanks
EDIT
Ok to update the question, I am now wondering if this is Guzzle/Laravel related. I changed the API to now point to the Twitter API and I am getting the same error after 80 synchronous requests.
I think it's better to use Asynchronous Request directly with Guzzle.
$request = new \GuzzleHttp\Psr7\Request('GET', 'https://api.coingecko.com/api/v3/coins?localization=false');
for ($i=0; $i < 50 ; $i++) {
$promise = $client->sendAsync($request)
->then(function ($response) {
echo 'I completed! ' . $response->getBody();
});
$promise->wait();
}
more information on Async requests: Doc
I have a similar problem as yours.
I doing the HTTP requests in the loop, and the first 80 requests are okay.
But the 81st start throwing this "Could not resolve host" exception.
It's very strange for me because the domain can be resolved perfectly fine on my machine.
Thus I start digging into the code.
End up I found that Laravel's Http facades keep generate the new client.
And I guess this eventually trigger the DNS resolver's rate limit?
So I have the workaround as following:
// not working
// as this way will cause Laravel keep getting a new HTTP client from guzzle.
foreach($rows as $row) {
$response = Http::post();
}
// workaround
$client = new GuzzleHttp\Client();
foreach($rows as $row) {
$response = $client->post();
// don't forget use $response->getBody();
}
i believe it's because $client will cached the DNS resolve result, thus it will reduce the call to DNS resolver and not trigger the rate limit?
I'm not sure whether it was right. BUT it's working for me.

Empty Response Web Service Doctrine 2 Slim 3

There is happening something really freaky, The following code is from a request I have in my Web Service using Slim 3 and Doctrine 2, I am geetting a empty response, I know for a fact the array it's getting filled but it is returning empty!
$data = $request->getParsedBody();
$intervention_items = $this->em->getRepository('App\Entity\V_Interventionitems')
->findAll(array('ic_interventiontype_id' => $data['it_id']));
foreach ($intervention_items as $int_items){
$data_response[] = $int_items->toArray();
//echo json_encode($int_items->toArray()); here prints the info right
}
return $response->withStatus(200)
->withHeader('Content-Type', 'application/json')
->write(json_encode($data_response)); //here returns empty
I know this issue is very amateur but I am invoking the Knoledge of my friends to help me out ;)
json_encode($data_response) is returning false. Use json_last_error() to find out what is wrong.

Instagram Real time API - PHP - Unable to get POST Update

I'm trying to connect with the instagram API, the connection works fine and I am receiving the updates just as described in the API documentation, the issue is that I cannot access to the data send to my callback function.
According to the doc
When someone posts a new photo and it triggers an update of one of your subscriptions, we make a POST request to the callback URL that you defined in the subscription
This is my code :
// check if we have a security challenge
if (isset ($_GET['hub_challenge']))
echo $_GET['hub_challenge'];
else // This is an update
{
// read the content of $_POST
$myString = file_get_contents('php://input');
$answer = json_decode($myString);
// This is not working starting from here
$id = $answer->{'object_id'};
$api = 'https://api.instagram.com/v1/locations/'.$id.'/media/recent?client_secret='.INSTA_CLI_SECRET.'&client_id='.INSTA_CLI_ID;
$response = get_curl($api); //change request path to pull different photos
$images = array();
if($response){
$decode = json_decode($response);
foreach($decode->{'data'} as $item){
// do something with the data here
}
}
}
Displaying the $myString variable I have this result, don't know why it is not decoded to json :(
[{"changed_aspect": "media", "subscription_id": 2468174, "object":
"geography", "object_id": "1518250", "time": 1350044500}]
the get_curl function is working fine when I hardcode my $id.
I guess something is wrong with my $myString, unfortunately the $_POST cvariable is not populated, Any idea what I am missing ?
Looking at the example JSON response included in your question, I can conclude that the object you are trying to talk with is wrapped in an array (hence the [ and ] around it in the JSON string).
You should access it using $answers[0]->object_id.
If that doesn't work, you can always use var_dump to check out the data in one of your variables.

What is the best way to check if table exists in DynamoDB?

What is the best way to check if table exists in DynamoDb?
I would appreciate it if the code would be in PHP.
Either active or not.
* Added later as an example to various cases for error code 400
It's very easy to check if the table exist, it can have one of the following
TableStatus => CREATING, ACTIVE, DELETING or UPDATING
but in case i get error 400 it can mean more than one thing.
1) sent null string as a table name by mistake.
[x-aws-body] => {"TableName":""}
)
[body] => CFSimpleXML Object
(
[__type] => com.amazon.coral.validate#ValidationException
[message] => The paramater 'tableName' must be at least 3 characters long and at most 255 characters long
)
[status] => 400
2) syntax error in the command sent to DynamoDB, for example writting tabel_name instead of table_name.
[x-aws-body] => {"TabelName":"test7"}
)
[body] => CFSimpleXML Object
(
[__type] => com.amazon.coral.validate#ValidationException
[message] => The paramater 'tableName' is required but was not present in the request
)
[status] => 400
3) I would guess but didn't check, if I exceed at that same time the provisioned capacity on the table.
You can have a look at "describe_table" of the official PHP SDK. 400 means "does not exist" There is a pretty extensive example in the official documentation. Look at how it is used in the "delete" example, right at the bottom.
http://docs.amazonwebservices.com/amazondynamodb/latest/developerguide/LowLevelPHPTableOperationsExample.html
Here is the (stripped) example from the doc
<?php
require_once dirname(__FILE__) . '/sdk/sdk.class.php';
$dynamodb = new AmazonDynamoDB();
$table_name = 'ExampleTable';
$response = $dynamodb->describe_table(array('TableName' => $table_name));
if((integer) $response->status !== 400)
{
$error_type = $response->body->__type;
$error_code = explode('#', $error_type)[1];
if($error_code == 'ResourceNotFoundException')
{
echo "Table ".$table_name." exists.";
}
}
?>
Some of these answers are using the older SDK's and so I thought I'd update this useful question with what I coded up and works well. The newer exceptions really do make this task easier. This function gives you a nice boolean to use in scripts.
use Aws\DynamoDb\Exception\ResourceNotFoundException; // <-- make sure this line is at the top
public function TableExists($tableName) {
$ddb = DynamoDbClient::factory(array('region' => 'us-east-1')); // EC2 role security
try {
$result = $ddb->describeTable(array(
"TableName" => $tableName
));
} catch (ResourceNotFoundException $e) {
// if this exception is thrown, the table doesn't exist
return false;
}
// no exception thrown? table exists!
return true;
}
Hopefully this complete working code helps some of you.
I think the answer that solves this with describeTable is a good one, but fooling around with the status code response makes the code less readable and more confusing.
I chose to check for a tables existence using listTables. Here are the docs
$tableName = 'my_table';
$client = DynamoDbClient::factory(array('region' => 'us-west-2'));
$response = $client->listTables();
if (!in_array($tableName, $response['TableNames'])) {
// handle non-existence.
// throw an error if you want or whatever
}
// handle existence
echo "Table " . $tableName . " exists";
With DynamoDB you need to parse the contents of the error message in order to know what type of error you received since the status code is almost always 400. Here is a sample function that could work to determine if a table exists. It also allows you to specify a status as well if you want to check if it exists and if it is in a certain state.
<?php
function doesTableExist(AmazonDynamoDB $ddb, $tableName, $desiredStatus = null)
{
$response = $ddb->describe_table(array('TableName' => $tableName));
if ($response->isOK()) {
if ($desiredStatus) {
$status = $response->body->Table->TableStatus->to_string();
return ($status === $desiredStatus);
} else {
return true;
}
} elseif ($response->status === 400) {
$error = explode('#', $response->body->__type->to_string());
$error = end($error);
if ($error === 'ResourceNotFoundException') {
return false;
}
}
throw new DynamoDB_Exception('Error performing the DescribeTable operation.');
}
Update: In the AWS SDK for PHP 2, specific exceptions are thrown by the DynamoDB client, making this way easier to handle. Also, there are "Waiter" objects, including one for this use case (see usage in the unit test) that is designed to sleep until the table exists.
The above answers are correct if you just want to know whether the table exist or not. I want to make another helpful point here in case.
One should be very careful in multi-threaded or production level code.
Assuming that one thread deleted the table, then you will still get the answer that the table exists in answer to your query from second thread, until the table is fully deleted. In such case, once the table is deleted, the table handle in second thread is zombie, like the dangling pointer error in C++.
With dynamodb cli you can do it very simple as follows:
aws dynamodb describe-table --table-name "my-table"
If the table exists it returns
0 -- Command was successful. There were no errors thrown by either the CLI or by the service the request was made to.
If the table does not exist it returns
255 -- Command failed. There were errors thrown by either the CLI or by the service the request was made to.
See also:
http://docs.aws.amazon.com/cli/latest/reference/dynamodb/describe-table.html
http://docs.aws.amazon.com/cli/latest/topic/return-codes.html

Twitter API always saying 400 Bad Request

I am using the following code to retrieve an amount of Tweets from the Twitter API:
$cache_file = "cache/$username-twitter.cache";
$last = filemtime($cache_file);
$now = time();
$interval = $interval * 60; // ten minutes
// Check the cache file age
if ( !$last || (( $now - $last ) > $interval) ) {
// cache file doesn't exist, or is old, so refresh it
// Get the data from Twitter JSON API
//$json = #file_get_contents("http://api.twitter.com/1/statuses/user_timeline.json?screen_name=" . $username . "&count=" . $count, "rb");
$twitterHandle = fopen("http://api.twitter.com/1/statuses/user_timeline.json?screen_name=$username&count=$count", "rb");
$json = stream_get_contents($twitterHandle);
fclose($twitterHandle);
if($json) {
// Decode JSON into array
$data = json_decode($json, true);
$data = serialize($data);
// Store the data in a cache
$cacheHandle = fopen($cache_file, 'w');
fwrite($cacheHandle, $data);
fclose($cacheHandle);
}
}
// read from the cache file with either new data or the old cache
$tweets = #unserialize(file_get_contents($cache_file));
return $tweets;
Of course $username and the other variables inside the fopen request are correct and it produces the correct URL because I get the error:
Warning: fopen(http://api.twitter.com/1/statuses/user_timeline.json?screen_name=Schodemeiss&count=5) [function.fopen]: failed to open stream: HTTP request failed! HTTP/1.1 400 Bad Request in /home/ellexus1/public_html/settings.php on line 187
that ^^ error returns whenever I try and open my page.
Any ideas why this might be? Do I need to use OAuth to even just get my tweets!? Do I have to register my website as somewhere that might get posts?
I'm really not sure why this is happening. My host is JustHost.com, but I'm not sure if that makes any diffrence. All ideas are welcome!
Thanks.
Andrew
PS. This code lies inside a function where username, interval and count are passed in correctly, hence in the error code its created a well formed address.
Chances are you are getting rate-limited
400 Bad Request: The request was invalid. An accompanying error
message will explain why. This is the status code will be returned
during rate limiting.
150 requests per hour for non authenticated calls (Based on IP-addressing)
350 requests per hour for authenticated calls (Based on the authenticated users calls)
You have to authenticate to avoid these errors popping up.
And also please use cURL when dealing with twitter. I've used file_get_contents and fopen to call the twitter API, and found that it is very unreliable. You would get hit with that every now and then.
Replace the fopen with
$ch = curl_init("http://api.twitter.com/1/statuses/user_timeline.json?screen_name=$username&count=$count");
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
$it = curl_exec($ch); //content stored in $it
curl_close($ch);
This may help
Error codes
https://developer.twitter.com/en/docs/basics/response-codes.html
Error codes defination is given in above link

Categories