My code is using file_get_contents() to make GET requests to an API endpoint. It looks like it is using HTTP/1.0 and my sysadmin says I need to use HTTP/1.1. How can I make an HTTP/1.1 request? Do I need to use curl or is there a better/easier way?
Update
I decided to use cURL since I am using PHP 5.1.6. I ended up forcing HTTP/1.1 by doing this:
curl_setopt($ch, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_1);
If I was using 5.3 or later I would have tried doing something like this:
$ctx = stream_context_create(array(
'http' => array('timeout' => 5, 'protocol_version' => 1.1)
));
$res = file_get_contents($url, 0, $ctx);
echo $res;
http://us.php.net/manual/en/context.http.php
Note: PHP prior to 5.3.0 does not
implement chunked transfer decoding.
If this value is set to 1.1 it is your
responsibility to be 1.1 compliant.
Another option I found which might provide HTTP/1.1 is to use the HTTP extension
I'd use cURL in either case, it gives you more control and in particular it gives you the timeout option. That's very important when calling an external API so as not to allow your application to freeze whenever a remote API is down.
Could like this:
# Connect to the Web API using cURL.
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, 'http://www.url.com/api.php?123=456');
curl_setopt($ch, CURLOPT_TIMEOUT, '3');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
$xmlstr = curl_exec($ch);
$http_code = curl_getinfo($ch, CURLINFO_HTTP_CODE);
curl_close($ch);
cURL will use HTTP/1.1 per default, unless you specify something else using curl_setopt($s,CURLOPT_HTTPHEADER,$headers);, where $headers is an array.
Just so others who want to use stream_context_create/file_get_contents know, if your server is configured to use keep-alive connections, the response will not return anything, you need to add 'protocol_version' => 1.1 as well as 'header' => 'Connection: close'. Example below:
$ctx = stream_context_create(array(
'http' => array(
'timeout' => 5,
'protocol_version' => 1.1,
'header' => 'Connection: close'
)
));
$res = file_get_contents($url, 0, $ctx);
Related
I am trying to get data from a REST api that uses a token for authentication in the URL. I can pass the relevant parameters in a web browser and get the data with no problem, however as soon as I try to do it in a PHP script with curl, I get a 401 error saying http authentication is required.
Have tried many different options but cannot get it to work, any help would be appreciated. My code is below, have removed site id and changed api_key
API documentation says:
The API can be accessed via HTTPS protocol only. SolarEdge monitoring server supports both HTTPS/1.0 and HTTPS/1.1 protocols.
All APIs are secured via an access token:
Every access to the API requires a valid token as a URL parameter named api_key.
For example: api_key= L4QLVQ1LOKCQX2193VSEICXW61NP6B1O
<?php
$curl = curl_init();
curl_setopt_array($curl, array(
CURLOPT_RETURNTRANSFER => 1,
CURLOPT_SSL_VERIFYPEER => 0,
CURLOPT_SSL_VERIFYHOST => 0,
CURLINFO_HEADER_OUT => 1,
CURLOPT_POST => 1,
CURLOPT_HTTPAUTH, CURLAUTH_ANY,
CURLOPT_URL => 'https://monitoringapi.solaredge.com/site/?????/energyDetails',
CURLOPT_USERPWD, 'api_key:6X0kjehfdgksdljsgksdjhfksdhfglksd',
CURLOPT_HTTPHEADER=>array(
"timeUnit:DAY",
"meters=PRODUCTION,CONSUMPTION",
"startTime:2017-12-09 00:00:00",
"endTime:2017-12-09 23:59:59"),
));
$resp = curl_exec($curl);
$status_code = curl_getinfo($ch, CURLINFO_HTTP_CODE);
// Close request to clear up some resources
curl_close($curl);
echo $resp;
echo "<br>$status_code";
//var_dump ($resp);
//print_r ($resp);
?>
Check the documentation again. Search for (Basic) Authentication.
You should look for something like this:
Authorization: <type> <credentials>
e.g. Authorization: Basic YWxhZGRpbjpvcGVuc2VzYW1l
The HTTP 401 Unauthorized client error status response code indicates that the request has not been applied because it lacks valid authentication credentials for the target resource.
This status is sent with a WWW-Authenticate header that contains information on how to authorize correctly.
This status is similar to 403, but in this case, authentication is possible.
Try your code again like this (add your API key) and report back the results:
$curl = curl_init();
curl_setopt_array($curl, array(
// CURLOPT_PORT=> 443,
// CURLOPT_RETURNTRANSFER => 1,
// CURLOPT_SSL_VERIFYPEER => 0,
// CURLOPT_SSL_VERIFYHOST => 0,
CURLINFO_HEADER_OUT => 1,
CURLOPT_POST => 1,
CURLOPT_HTTPAUTH, CURLAUTH_ANY,
CURLOPT_URL => 'https://monitoringapi.solaredge.com/site/?????/energyDetails',
CURLOPT_USERPWD, 'api_key:6X0kjehfdgksdljsgksdjhfksdhfglksd',
CURLOPT_HTTPHEADER=>array(
"timeUnit:DAY",
"meters=PRODUCTION,CONSUMPTION",
"startTime:2017-12-09 00:00:00",
"endTime:2017-12-09 23:59:59"),
));
$resp = curl_exec($curl);
$status_code = curl_getinfo($curl, CURLINFO_HTTP_CODE);
curl_close($curl);
echo $resp;
echo "<br>$status_code";
I am trying to use Slack Custom command and not pretty sure how to use delayed messages since the Yoda Speak External API takes more than 3 seconds to respond.
I have done the following:
Sent the slack command /Yoda in my case and received the reponse_url.
Used the following to post the following to the response URL.
$data_string = '{"response_type": "in_channel", "text":"Checking,please wait..."}' ;
$chs = curl_init();
curl_setopt($chs, CURLOPT_URL, $response_url);
curl_setopt($chs, CURLOPT_POST, true);
curl_setopt($chs, CURLOPT_POSTFIELDS, $data_string);
curl_setopt($chs, CURLOPT_SSL_VERIFYPEER, FALSE);
curl_setopt($chs, CURLOPT_SSL_VERIFYHOST, FALSE);
curl_setopt($chs, CURLOPT_RETURNTRANSFER, true);
curl_setopt($chs, CURLOPT_POST, 1);
curl_setopt($chs, CURLOPT_HTTPHEADER, array('Content-Type:application/json'));
$results = curl_exec($chs);
Now, when I call the Yoda API, it gives the following error "Timeout was reached". I read about delayed responses but not sure how should I proceed from here.
$chsres = curl_init();
curl_setopt($chsres, CURLOPT_URL, "https://yoda.p.mashape.com/yoda?sentence=welcome+to+stack");
curl_setopt($chsres, CURLOPT_SSL_VERIFYPEER, FALSE);
curl_setopt($chsres, CURLOPT_SSL_VERIFYHOST, FALSE);
curl_setopt($chsres, CURLOPT_VERBOSE, true);
curl_setopt($chsres, CURLOPT_TIMEOUT, 45);
curl_setopt($chsres, CURLOPT_RETURNTRANSFER, true);
curl_setopt($chsres, CURLOPT_HTTPHEADER, array('Content-Type:application/json', "X-Mashape-Key:> deMeGoBfMvmshQSemozTqJEY9z0jp1eIhuAjsnx9cQAQsHUifD"));
$resultchsres = curl_exec($chsres);
echo $resultchsres;
Can someone please let me know how to get rid of the timeout error using delayed responses?
UPDATED CODE:
$response_url = $_POST['response_url'];
$text = $_POST['text'];
$term = str_replace(' ', '+', $text);
//https://paypal.slack.com/services/B0VQMHX8W#service_setup
//initial respond with 200OK for timeout
ignore_user_abort(true);
set_time_limit(0);
ob_start();
echo('{"response_type": "in_channel", "text": "Checking, please wait..."}');
header($_SERVER["SERVER_PROTOCOL"] . " 200 OK");
header("Content-Type: application/json");
header('Content-Length: '.ob_get_length());
ob_end_flush();
ob_flush();
flush();
$chsres = curl_init();
curl_setopt_array($chsres, array(
CURLOPT_URL => "https://yoda.p.mashape.com/yoda?sentence=$term",
CURLOPT_SSL_VERIFYPEER => FALSE,
CURLOPT_SSL_VERIFYHOST => FALSE,
CURLOPT_VERBOSE => true,
CURLOPT_RETURNTRANSFER => FALSE,
CURLOPT_HTTPHEADER => array('Content-Type:application/json', "X-Mashape-Key: deMeGoBfMvmshQSemozTqJEY9z0jp1eIhuAjsnx9cQAQsHUifD"),
CURLOPT_RETURNTRANSFER => true
));
$yodaresponse = curl_exec($chsres);
$curl = curl_init();
curl_setopt_array($curl, array(
CURLOPT_URL => $response_url,
CURLOPT_POST => 1,
CURLOPT_RETURNTRANSFER => true,
CURLOPT_POSTFIELDS => $yodaresponse
));
$resp = curl_exec($curl);
var_dump($resp);
curl_close($curl);
I still get the same error "Darn – that slash command didn't work (error message: Timeout was reached). Manage the command at slash-command"
You're doing all the right things, just need to change the order.
Respond to the original request with a 200 OK response immediately. See this answer for details, but essentially:
ignore_user_abort(true);
ob_start();
echo('{"response_type": "in_channel", "text": "Checking, please wait..."}');
header($_SERVER["SERVER_PROTOCOL"] . " 200 OK");
header("Content-Type: application/json");
header('Content-Length: '.ob_get_length());
ob_end_flush();
ob_flush();
flush();
Then make the Yoda API request using curl, as you're doing
Once you have the Yoda results, send them to Slack at $response_url using curl, as you're doing.
If you're using FPM then this is what you want - http://php.net/manual/en/function.fastcgi-finish-request.php
Your code would then look like this...
<?php
$response_url = $_POST["response_url"];
$term = rawurlencode($_POST["text"]);
error_log("POST: " . print_r($_POST, 1));
$response = ["response_type"=>"in_channel", "text"=>"Checking, please wait..."];
echo json_encode($response);
header("Content-Type: application/json");
fastcgi_finish_request();
$ch = curl_init();
...
Another approach that will work is to use a curl request with a short timeout to spawn a second PHP script. Since my provider has put some restrictions on my PHP environment (e.g. no process spawning) this has been the only approach that has worked for me.
The first script will terminate shortly after and send an HTTP OK back to Slack. The second script will continue running, handle the time consuming processing (e.g. calling external APIs) and finally send the result as delayed response to the response_url.
1st script
This is the curl request in your first script:
<?php>
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, "second.php?redirect_url=$redirect_url");
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_FOLLOWLOCATION, true);
curl_setopt($ch, CURLOPT_TIMEOUT_MS, 400); //just some very short timeout
curl_setopt($ch, CURLOPT_NOSIGNAL, 1);
curl_exec($ch);
curl_close($ch);
/* send short response back to user, e.g. "Processing your request..." */
?>
The length of the timeouts is arbitrary, however in my tests a very short timeout (e.g. 10ms) did not work.
You will also need to implement a way to transfer input data between the two scripts as illustrated with passing the request_url as URL parameter.
Finally for slash commands Slack requires you to send a short response back to the user.
2nd script
This is how your 2nd script looks like:
<?php
ignore_user_abort(true); //very important!
usleep (500000); //to ensure 2nd script responds after 1st
/* call external API */
/* send response back to Slack using response_url */
?>
The statement ignore_user_abort(true); is mandatory to ensure your 2nd script keeps running after the curl timeout.
The usleep with 0.5 secs is to ensure that the 2nd script responds after the first, but not mandatory for this solution to work.
The example is based on one answer of the "Continue PHP execution after sending HTTP response" question.
From what I can see in the documentation, you're doing things mostly correctly. Just by echoing anything out, you're already passing a 200 OK message, so no need to do it explicitly. You should check to make sure this isn't a server problem though; is the URL being posted to valid? Not getting mangled by a rewrite rule along the way?
I've made some changes to your code below, including some debugging that will go to your error log (i.e. Apache's error log, by default.) Give it a try, and at the very least you'll have some more debugging details.
<?php
$response_url = $_POST["response_url"];
$term = rawurlencode($_POST["text"]);
error_log("POST: " . print_r($_POST, 1));
ob_end_clean();
ob_start();
$response = ["response_type"=>"in_channel", "text"=>"Checking, please wait..."];
echo json_encode($response);
header("Content-Type: application/json");
header("Content-Length: " . ob_get_size());
ob_end_flush();
flush();
$ch = curl_init();
curl_setopt_array($ch, [
CURLOPT_URL => "https://yoda.p.mashape.com/yoda?sentence=$term",
CURLOPT_HTTPHEADER => ["X-Mashape-Key: deMeGoBfMvmshQSemozTqJEY9z0jp1eIhuAjsnx9cQAQsHUifD"],
CURLOPT_RETURNTRANSFER => true
]);
$yodaresponse = curl_exec($ch);
curl_close($ch);
error_log("Yoda response: $yodaresponse");
$yodajson = json_encode([
"response_type"=>"in_channel",
"text"=>$yodaresponse
]);
$ch = curl_init();
curl_setopt_array($ch, [
CURLOPT_URL => $response_url,
CURLOPT_POST => 1,
CURLOPT_HTTPHEADER => ["Content-Type: application/json"],
CURLOPT_RETURNTRANSFER => true,
CURLOPT_POSTFIELDS => $yodajson
]);
$resp = curl_exec($ch);
curl_close($ch);
error_log("API response: $resp");
Posting an answer as I don't have enough reputation to post comments...
I've had the same problem, and then I realized that Slack treats requests and responses differently. Specifically, HTTP request and response differ in their first line.
HTTP request example:
GET /hello.htm HTTP/1.1
User-Agent: Mozilla/4.0 (compatible; MSIE5.01; Windows NT)
Host: www.tutorialspoint.com
Accept-Language: en-us
Accept-Encoding: gzip, deflate
Connection: Keep-Alive
HTTP response example:
HTTP/1.1 200 OK
Date: Mon, 27 Jul 2009 12:28:53 GMT
Server: Apache/2.2.14 (Win32)
Last-Modified: Wed, 22 Jul 2009 19:15:56 GMT
Content-Length: 11
Content-Type: text/xml
Connection: Closed
hello there
If you can access raw bytes to be sent in PHP (never used PHP, so not familiar), just make it look like a response, rather than a request. Otherwise, send a response immediately, then do the work that you need, and send a request with the new message. This can be done in number of ways, one of which was outlined by #miken32, I opted out for invoking a background process in python.
I am using php and I want to create a HTTP request to access some API data. I have a document that says, I need to place the following request
GET /abc/api/Payment HTTP/1.1
Content-Type: application/x-www-form-urlencoded
X-PSK: [App Key]
X-Stamp: [UTC Timestamp]
X-Signature: [HMACSHA256 base 64 string]
Body:
var1, var1
I have app key, I can get UTC Timestamp and I can create signature. I am not sure how to start creating this request? I am using codeingiter. If someone can help with example to set the header and body?
I also tried this url https://www.hurl.it/ to place requests but can't make it work. Any suggestions?
You want to use cURL's CURLOPT_HTTPHEADER option.
http://php.net/manual/en/function.curl-setopt.php
This should get you started
function request($url) {
$ch = curl_init();
$curlOpts = array(
CURLOPT_URL => $url,
CURLOPT_RETURNTRANSFER => true,
CURLOPT_HTTPHEADER => array(
"Content-Type: application/x-www-form-urlencoded",
"X-PSK: [App Key]",
"X-Stamp: [UTC Timestamp]",
"X-Signature: [HMACSHA256 base 64 string]"
),
CURLOPT_FOLLOWLOCATION => true
);
curl_setopt_array($ch, $curlOpts);
$answer = curl_exec($ch);
// If there was an error, show it
if (curl_error($ch)) die(curl_error($ch));
curl_close($ch);
return $answer;
}
This question already has answers here:
Closed 10 years ago.
Possible Duplicate:
Drupal login via Rest server
I have been using this code for file get contents with post data but receiving an error
Warning: file_get_contents(http://50.116.19.49/rest/user/login.json): failed to open
stream: HTTP request failed! HTTP/1.0 406 Not Acceptable: Unsupported request content type
application/x-www-form-urlencoded in C:\xampp\htdocs\post.php on line 20
My code is
<?php
$postdata = http_build_query(
array(
'var1' => 'myuser',
'var2' => 'pwd'
)
);
$opts = array('http' =>
array(
'method' => 'POST',
'header' => 'Content-type: application/x-www-form-urlencoded',
'content' => $postdata
)
);
$context = stream_context_create($opts);
$result = file_get_contents('http://50.116.19.49/rest/user/login.json', false,
$context);
?>
Can anybody help in this Thanks in advance.
we can use curl instead of function file_get_contents($request);
Here is the code of curl :
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL,$request);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
curl_setopt($ch, CURLOPT_TIMEOUT, 15);
curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, 0);
$xml_response = curl_exec($ch);
where $request is your url.
This particular server, as it seems, expects to see JSON in the POST data when you call /login.json, so you should rewrite a few things in your code.
Change the $postdata construction:
$postdata = json_encode(array(
'var1' => 'myuser',
'var2' => 'pwd'
));
Change the Content-Type header:
$opts = array('http' => array(
'method' => 'POST',
'header' => 'Content-type: application/json',
'content' => $postdata
));
file_get_contents returns FALSE on error with the HTTP stream wrapper as well. An error means any error condition with the HTTP response, for example a HTTP status code from the 400 range like 406 Not Acceptable: Unsupported request content type in your case.
You can disable the "FALSE on Error" behavior by setting the ignore_errorsHTTP context option to TRUE:
'ignore_errors' = TRUE,
You will then get the response body of the request as the result in error cases, too.
To obtain the status code itself you can make use of the special $http_response_header variable.
For a discussion of these settings and how to parse response headers, please see HEAD first with PHP Streams. However in your case the response body might already contain more information about the problem.
In your specific problem you need to double check that the encoding of the request is supported by the server. As I don't know your server, I can not say much about that. The reference to the error code might shed some light for you. There seems to be a problem with the Content-type: application/x-www-form-urlencoded you are using.
For example as other clever folks on this site have told me, that endpoint is Drupal. If so, the following was suggested in a similar question:
You have to enable application/x-www-form-urlencoded content type of your service endpoint.
Do as follows: Services -> Edit Resources -> select tab "Server" -> enable "application/x-www-form-urlencoded" and that's it.
Hopefully this is of help to you.
I am completely new PHP and want a client program to call an URL web service.I am using file_get_content to get the data.How do add additional headers to the request made using file_get_content.
I also was thinking of using cURL. I wanted to know how cURL can be used to do a GET request.
You can add headers to file_get_contents, it takes a parameter called context that can be used for that:
$context = stream_context_create(array(
'http' => array(
'method' => 'GET',
'header' => "Host: www.example.com\r\n" .
"Cookie: foo=bar\r\n"
)
));
$data = file_get_contents("http://www.example.com/", false, $context);
As for cURL, the basic example from the PHP manual shows you how to perform a GET request:
<?php
// create a new cURL resource
$ch = curl_init();
// set URL and other appropriate options
curl_setopt($ch, CURLOPT_URL, "http://www.example.com/");
curl_setopt($ch, CURLOPT_HEADER, 0);
// grab URL and pass it to the browser
curl_exec($ch);
// close cURL resource, and free up system resources
curl_close($ch);
?>