I'm having some trouble POSTing data from a client machine to our internal site using PHP. The server accepts data via HTTPS with basic authentication. Here is the code I'm using to POST with:
$parameters = array('http' => array(
'method' => 'POST',
'content' => $data
)
);
if ($optionalHeaders !== NULL) {
$parameters['http']['header'] = $optionalHeaders;
}
$ctx = stream_context_create($parameters);
$fp = fopen($url, 'rb', false, $ctx);
With the following header:
$postHeader = "POST /index.php HTTP/1.1\r\n".
"Host:my.host\r\n".
"Content-Type: application/x-www-form-urlencoded\r\n".
"User-Agent: PHP-Code\r\n".
"Content-Length: " . strlen($postData) . "\r\n".
"Authorization: Basic ".base64_encode($user.':'.$password)."\r\n".
"Connection: close\r\n";
Now, I can get this to work, and it posts just fine on one of my clients with PHP version 5.2.5, but the on another client I get this error message:
fopen(magical_url): failed to open stream: HTTP request failed! HTTP/1.1 400 Bad Request
And Apache error log gives:
request failed: error reading the headers
The only difference I can see is that the latter client has PHP version 5.1.6.
Does anyone know if this is a bug? Or am I doing something wrong somewhere...
I've looked through the PHP site and found this bug listed for version 5.2.6 of PHP https://bugs.php.net/bug.php?id=45540 but this post-dates the version it works on!
Thanks,
Jak
You should not provide all those headers. That's bound to fail.
Headers like POST, Host, Content-Length and Connection are automatically provided. Remove all your optional headers (should work then) and add them step by step.
Related
I'm trying to use the discogs php api in a PHP script to get info on a release using the release ID. For example, when I make a request to:
http://mywebsite.com/test.php?id=1017868
I want to call the discogs API to get info on the release with id = 1017868. I can see the info I want by manually going to:
https://api.discogs.com/releases/1017868
So I have my $consumerKey =and $consumerSecret and I'm following the DISCOGS AUTH FLOW instructions, which says I can send my keys in a get request like so:
curl "https://api.discogs.com/database/search?q=Nirvana" -H "Authorization: Discogs key=foo123, secret=bar456"
I'm trying to make a get request for my target id in my php script like so:
<?php
echo "hello world <br>";
//discogs simple auth flow, http requests
$remote_url = 'https://api.discogs.com/releases/1017868';
// Create a stream
$opts = array(
'http'=>array(
'method'=>"GET",
'header' => "Authorization: Discogs key=mykeyasdlkhaskld, secret=mysecretkeykjnasdkjnsadkj"
)
);
$context = stream_context_create($opts);
// Open the file using the HTTP headers set above
$file = file_get_contents($remote_url, false, $context);
print($file);
echo "end of program";
?>
But I keep getting the error:
"failed to open stream: HTTP request failed! HTTP/1.1 403 Forbidden".
Is there something I'm forgetting for making my request to the server? Thanks.
I have made the same request in Postman and it gives me the correct response, but when i make the same call from my website it crashes.
Im simply trying to make a GET request from PHP with one header, the oauth2 authorization token.
Code (googleLogin.php):
<?php
$url = "https://www.googleapis.com/oauth2/v1/userinfo?alt=json";
$options = array(
'http'=>array(
'method'=>"GET",
'header'=>"Accept-language: en\r\n" .
"Authorization: Bearer " . $_GET["access_token"] . "\r\n"
)
);
$context = stream_context_create($options);
$file = file_get_contents($url, false, $context);
print_r($file);
?>
Error:
PHP Warning:
file_get_contents(https://www.googleapis.com/oauth2/v1/userinfo?alt=json):
failed to open stream: HTTP request failed! HTTP/1.0 401 Unauthorized
in /home/shawn/public_html/cloud/php/googleLogin.php on line 14
I dont know if this error implies (i) The file googleLogin.php on the web server doesnt have correct permissions (i tried 777), (ii) The access token is expired/invalid (it cant be because i tried the same token 2 seconds later successfully with Postman), or (iii) php is formatting my HTTP request incorrectly
I call myself an experienced PHP developer, but this is one drives me crazy. I'm trying to get release informations of a repository for displaying update-warnings, but I keep returning 403 errors. For simplifying it I used the most simple usage of GitHubs API: GET https://api.github.com/zen. It is kind of a hello world.
This works
directly in the browser
with a plain curl https://api.github.com/zen in a terminal
with a PHP-Github-API-Class like php-github-api
This works not
with a simple file_get_contents()from a PHP-Skript
This is my whole simplified code:
<?php
$content = file_get_contents("https://api.github.com/zen");
var_dump($content);
?>
The browser shows Warning: file_get_contents(https://api.github.com/zen): failed to open stream: HTTP request failed! HTTP/1.0 403 Forbidden, the variable $content is a boolean and false.
I guess I'm missing some sort of http-header-fields, but neither can I find those informations in the API-Docs, nor uses my terminal curl-call any special header files and works.
This happens because GitHub requires you to send UserAgent header. It doesn't need to be anything specific. This will do:
$opts = [
'http' => [
'method' => 'GET',
'header' => [
'User-Agent: PHP'
]
]
];
$context = stream_context_create($opts);
$content = file_get_contents("https://api.github.com/zen", false, $context);
var_dump($content);
The output is:
string(35) "Approachable is better than simple."
I am trying to make a CURL request with some basic authentication (through sending an encrypted header). However, when I try to set the "Host: _" header, the remote server responds with:
Bad Request: Your browser sent a request that this server could not understand.
Here's the code that makes the CURL call. Note that it works as soon as I comment out the "Host: url" header in the $http_header variable. However, it is used on the target server as part of the authentication procedure, so I can not simply remove it.
$curl = curl_init();
$opt = array(
CURLOPT_URL=>$url,
CURLOPT_RETURNTRANSFER=>1,
CURLOPT_HTTPHEADER=>$http_header,
CURLOPT_POST=>1,
CURLOPT_POSTFIELDS=>$data_params,
CURLOPT_CONNECTTIMEOUT=>5, // Timeout to 5 seconds
);
curl_setopt_array($curl, $opt);
// $output contains the output string
$output = curl_exec($curl);
// it closes the session
curl_close($curl);
The contents of $http_header (an associative array):
array
0 => string 'Host: http://localhost/work/myproject'
1 => string 'Content-Type: multipart/form-data'
2 => string 'X-Authorization: Basicauth Z2xZjA2YzJhMzIxYTI1ZmIzZTgxYQ=='
The Host header doesn't accept a full URL, but only a hostname.
In this case the solution is to replace this header:
"Host: $url"
with:
"Host: ". parse_url($url, PHP_URL_HOST) ."
The Host header takes a hostname, not a URL. i.e. localhost, not http://localhost/work/myproject.
cURL should be generating it automatically from CURLOPT_URL though.
The Host header should not contain the protocol, only the host's name. So in this case all you need is localhost:
'Host: localhost'
I don't think either answer answers the asker's question. I've ran into the same problem. From looking at what cURL is sending, it is sending it's own Host header. This means that when you add one you now have two. All servers MUST respond with a 400 error when they see more than one host header.
The trick is get cURL not to generate it's own header (which I haven't figured out how to do yet) or don't send yours. In your case it seems not sending yours is fine. But, for what I'm doing, I have to send mine.
I was trying to make an RESTful API call to upload videos through POST method. What I am lacking is that I don't know the best practices for writing this kind of API as well I don't find any resource on the internet to follow. Right now I am doing this:
I am working in PHP and zend framework ( Zend_Rest_Route ).
First approach:
using file_get_contents on client side and POST it to API using curl, and on server side using file_put_contents to write that data and sending an appropriate response.
Second:
using Zend_File_Treansfer to receive file at server side, and putting address of my upload api end point in zend_form with setting method as post. In this case file is uploaded to server, but after submitting the form, the url in address bar points to the api server and never comes back to the form.
Am I doing it right?, if not do let me know what are the best practices and how to accomplish this.
Thank you for your time.
Something like this worked for me:
public function postAttachment($fileName, $fileMimetype, $fileContents, $postURL, $username, $password)
{
$auth = base64_encode($username . ':' . base64_decode($password));
$header = array("Authorization: Basic ".$auth);
array_push($header, "Accept: */*");
$boundary = "----------------------------".substr(md5(rand(0,32000)), 0, 12);
$data = "";
$data .= "--".$boundary."\r\n";
//Collect Filedata
$data .= "Content-Disposition: form-data; name=\"file\"; filename=\"".$fileName."\"\r\n";
$data .= "Content-Type: ".$fileMimetype."\r\n";
$data .= "\r\n";
$data .= $fileContents."\r\n";
$data .= "--".$boundary."--";
// add more parameters or files here
array_push($header, 'Content-Type: multipart/form-data; boundary='.$boundary);
$params = array('http' => array(
'method' => 'POST',
'protocol_version' => 1.1,
'user_agent' => 'File Upload Agent',
'header' => $header,
'content' => $data
));
$ctx = stream_context_create($params);
$fp = fopen($postURL, 'rb', false, $ctx);
if (!$fp) {
throw new Exception("Problem with ".$postURL." ".$php_errormsg);
}
$responseBody = #stream_get_contents($fp);
if ($responseBody === false) {
throw new Exception("Problem reading data from ".$postURL.", ".$php_errormsg);
}
}
If you want to post several files, or add other multi-part parameters, it's easy to add these in other boundaries too.
I found some of this code on another post, and you can probably find similar code in the PHP wiki (http://www.php.net/manual/en/function.stream-context-create.php#90411). BUT ... That code was not properly handling the carriage return + line feeds and my server was summarily rejecting that post. In addition, that the older code was also using HTTP version 1.0 -- (which does not re-use sockets). When using HTTP 1.1 sockets are re-used when posting lots of files. (This works with HTTPS too.) I added my own user agent - If your are tricking some server into thinking this is a browser post, you might want to change the user agent to spoof a browser.
have you tried adding a redirect to end of your controller action that handles the upload? (if not you really should as its good practice to redirect after post) (make sure you redirect AFTER your logic has executed). In essence the 'page' that receives the post data should just work on the data, and any information you want to return to the user about that post action should be given to them on the page you redirect them to.
[form]--POST-->['post' controller action]--redirect (302)-->[page with success/failure info]