PHP CURL: Manually setting the content-length header - php

Say I upload a file with PHP, CURL:
$postData = array();
$postData['file_name'] = "test.txt";
$postData['submit'] = "UPLOAD";
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, $url );
curl_setopt($ch, CURLOPT_RETURNTRANSFER,1);
curl_setopt($ch, CURLOPT_POST, 1 );
curl_setopt($ch, CURLOPT_POSTFIELDS, $postData );
Now assume I have to manually set the content-length header.
$headers=array(
"POST /rest/objects HTTP/1.1",
'accept: */*',
"content-length: 0" //instead of 0, how could I get the length of the body from curl?
)
curl_setopt($ch, CURLOPT_HTTPHEADER, $headers); //set headers
$response = curl_exec($ch);
How would I measure the size of the body? (just specifying the filesize as content length doesn't seem to work)
In another example what if the body contains data that is not an actual file. (manually set by postfields) In that situation how would I get the content length of the body?
Thanks for any light shed on this, it appears to be a tough issue.

To get the length of a post body, try formatting the fields int a GET style string (aka param1=value1&param2=value2) then setting that string as the CURL_POSTFIELDS with curl_setopt. An array does not have to be supplied. You can simply use strlen() to get the value to use for the content-length header.
If you are posting a file (or files) in addition to other fields, as you appear to be in the example above, you have to supply the value for the file as #/path/to/file, then get the filesize in bytes and add that to the total content-length.
So for the above example, assuming the file test.txt is in the /test dir of your server, the post value string would be file_name=#/test/text.txt&submit=UPLOAD. You MUST url_encode this string as well, before you assign it as the curl post value. To get the content length you get the length of that string (post url-encoding) and add it to the filesize of /test/test.txt.

That sounds wrong. The data generated will include sub-headers and Content-Length needs to also include those sub-headers. And since the sub-headers include a boundary, may include this and that data, this whole thing cannot work (the size cannot be specified without knowing exactly what the complete request is going to be.)
Actually, the only one that can compute the size, from what I can see is cURL itself. But it doesn't do it 8-P

Related

Convert a VBS script to PHP

I have been given an example piece of code from a company I'm dealing with for how to post XML data to a URL then read the response. Unfortunately for me this in VBS which I don't have a good working knowledge of:
This is the section of code that I'm interested in. This should pass over the XML file that was read in to oXML then post it and read the response:
set oHTTP = CreateObject("Microsoft.XMLHTTP")
oHTTP.open "POST", "http://www.ophub.net/opxml/response.asp", false,00092,QW 'file url - with dealers Account number, Password
oHTTP.setRequestHeader "Content-Type", "application/x-www-form-urlencoded"
oHTTP.setRequestHeader "Content-Length", Len(sRequest)
oHTTP.send oXML
From what I understand of this in PHP this can be done with cUrl and I have come up with the following from bits that I have read online but this doesn't work and I'm not sure why.
$ch = curl_init();
curl_setopt($ch, CURLOPT_HTTPHEADER, Array("Content-Type: application/x-www-form- urlencoded"));
curl_setopt($ch, CURLOPT_URL, "http://www.ophub.net/opxml/response.asp");
curl_setopt($ch, CURLOPT_USERPWD, "00092:QW");
curl_setopt($ch, CURLOPT_POST, 1);
curl_setopt($ch, CURLOPT_POSTFIELDS, "XML=" . $xml);
$content=curl_exec($ch);
echo $content;
I'm sure I can't be far off what I need but I can't seem to get there so any hep would be very much appreciated.
In your post fields just set the xml no need to set xml=
curl_setopt($ch, CURLOPT_POSTFIELDS, $xml);
you also need return transfer true
curl_setopt($ch, CURLOPT_RETURNTRANSFER, TRUE);
It looks to me like that is precisely what you need. It seems to be an exact translation.
The only two potential issues that I can see are:
You PHP code seems to have a space in the middle of the Content-Type header value, this will most likely break things
You will, if you haven't already done so, need to URL encode the $xml data before sending it as part of a application/x-www-form-urlencoded message (clue's in the type name :-P), which you can do with urlencode().
It might be a good idea to build the body data as a string and echo it out, to ensure that the data is correct:
echo 'XML=' . urlencode($xml);
For the record wrapping XML messages in application/x-www-form-urlencoded is a horrible way to do things. But I've come across more than one API that does it, so I'm going to assume that your code is correct in this regard for the API you are trying to consume.

uploading a local document using php

I'm trying to test a rest api for upload a local file to a https url. I am currently using curl to do it. The problem is that my POST in curl is getting converted into PUT (http://curl.haxx.se/docs/manpage.html) mentions the same.
To test from command line I'm using the following:
curl -H "Content-Type: application/json" -H "Authorization:Bearer XXXXXXXXXXXXXXXYYYYYzzzzzz" -H "Accept-Version: ~1" -H "Except: 100-continue" -H "Accept-Version: ~1 " -T "somefile.pdf" "https://abc-xyz.co/docs"
Output:
{"code":"BadMethod","message":"/docs does not support PUT"}
my php code:
$url = '"https://abc-xyz.co/docs';
$filepath = '/Users/me/Documents/somefile.pdf'; //mac path
$ch = curl_init($url);
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, "POST" );
curl_setopt($ch, CURLOPT_HTTPHEADER,
array('Authorization: Bearer XXXXXXXXXXXXXXXYYYYYzzzzzz', 'Accept-Version: ~1', 'Except: 100-continue');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLINFO_HEADER_OUT, true);
$file = "#".$filepath;
curl_setopt($ch, CURLOPT_POSTFIELDS,$file);
$response = curl_exec($ch);
if(!CURL_ERROR($ch))
{
echo"\n The output is:".$response;
}
else
{
echo "\nUpload failed ".curl_error($ch);
exit;
}
curl_close($ch);
Output: I get parse error.
Can some one educate me with 3 things:
Can I use curl to do POST for https? if yes, how? and if no what should my approach be?
How to pass the file path name in my php? I tried "#".$filepath too but it still doesn't like it.
If I want to restrict my file upload type to be only pdf should i be using mime type? (application/pdf????)
Thanks,
DR
From the commandline, see post fileupload with curl.
From PHP, see the manual
The full data to post in a HTTP "POST" operation. To post a file, prepend a filename with # and use the full path. The filetype can be explicitly specified by following the filename with the type in the format ';type=mimetype'. This parameter can either be passed as a urlencoded string like 'para1=val1&para2=val2&...' or as an array with the field name as key and field data as value. If value is an array, the Content-Type header will be set to multipart/form-data. As of PHP 5.2.0, value must be an array if files are passed to this option with the # prefix.
curl_setopt($ch, CURLOPT_POSTFIELDS,array('someuploadname'=> "#".$filepath));

Zend_Http_Client - Why does my POST request send the wrong content-type header?

I'm making a call to an external service using the Zend Http client. The service allows me to upload files to their storage system. It requires relevant params (userid, etc.) to be sent in the query string, and the file upload content should be sent in the POST body with a content-type of "application/zip" (I'm sending it a zip file with various things in it).
To do this, I set the params in the query string using the zend client's setParameterGet() function. I then set the file upload content using the setFileUpload() function:
$this->client->setFileUpload($zipFilePath, 'content', null, 'application/zip');
However, the service is telling me that I'm sending it the wrong content type, which is "multipart/form-data"
Here are the raw headers that the Zend client is sending to the service (note that I've removed bits of sensitive information, replacing them with item names enclosed in [] brackets)
POST
https://[ServiceURL]?cmd=[COMMAND]&enrollmentid=[ENROLLMENTID]&itemid=[ITEMID]
HTTP/1.1
Host: [HOST] Accept-encoding: gzip, deflate
User-Agent: Zend_Http_Client Cookie:
AZT=9cMFAIBgG-eM1K|Bw7Qxlw7pBuPJwm0PCHryD;
Content-Type: multipart/form-data; boundary=---ZENDHTTPCLIENT-05535ba63b5130ab41d9c75859f678d8
Content-Length: 2967
-----ZENDHTTPCLIENT-05535ba63b5130ab41d9c75859f678d8
Content-Disposition: form-data; name="content";
filename="agilixContent.zip"
Content-Type: application/zip
[RAW FILE DATA HERE]
So basically, even though I've set the POST content type header, my external service is telling me I've sent the wrong content type, because there is another content-type header with the value "multipart/form-data". I've tried changing/removing that content header, but to no avail. How can I remove that header so that I won't have these two duplicate "content-type" headers in my requests?
If you want to upload a file using "application/zip" as content type you should not use ->setFileUpload() but rather ->setRawData(). setFileUpload() is used to mimick HTML form based file uploads which is not what you need.
See http://framework.zend.com/manual/en/zend.http.client.advanced.html#zend.http.client.raw_post_data for more information. What you need (based on your original example) will be something like:
$zipFileData = file_get_contents($zipFilePath);
$this->client->setRawData($zipFileData, 'application/zip');
$response = $this->client->request('POST');
Note that if your ZIP file may be very big (say more than a few megabytes) you may want to use ZHC's streaming support features, so avoid memory hogging. If you know your files are always less than 5-10 megabytes, I wouldn't bother with it though.
I am not sure how you can do that with Zend HTTP Client but I am sure you could do that with plain cURL. As you must be knowing cURL gives you lot of flexibility and I have not dig in to Zend but there are chances that Zend might be using cURL internally.
<?php
// URL on which we have to post data
$url = "http://localhost/tutorials/post.php";
// Any other field you might want to catch
$post_data = "khan";
// File you want to upload/post
//$post_data['zip_file'] = "#c:/foobar.zip";
$headers[] = "Content-Type: application/zip";
// Initialize cURL
$ch = curl_init();
// Set URL on which you want to post the Form and/or data
curl_setopt($ch, CURLOPT_URL, $url);
// Data+Files to be posted
curl_setopt($ch, CURLOPT_POSTFIELDS, $post_data);
// Set any custom header you may want to set or override defaults
curl_setopt($ch, CURLOPT_HTTPHEADER, $headers);
// Pass TRUE or 1 if you want to wait for and catch the response against the request made
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
// For Debug mode; shows up any error encountered during the operation
curl_setopt($ch, CURLOPT_VERBOSE, 1);
// Execute the request
$response = curl_exec($ch);
// Just for debug: to see response
echo $response;
I hope above snippet will work for you. That's bit modified code from my blog post mentioned below.
Reference: http://blogs.digitss.com/php/curl-php/posting-or-uploading-files-using-curl-with-php/

How to set multiple Content-Types?

How to set multiple Content-Types? I have to pass an array to request body. That array contains a text and an image in binary.
curl_setopt($apiCon, CURLOPT_POSTFIELDS, array(
'status' => rawurlencode($status),
'photo' => $photoInBinary
));
curl_setopt($apiCon, CURLOPT_HTTPHEADER, array(
'Host: api.mixi-platform.com',
'Authorization: OAuth ' . $accessToken,
'Content-Type: multipart/form-data'
));
The problem is the host doesn't understand the format of the image, so I need to pass more content type 'image/jpg' but I don't know where to put it.
The above code works but it posts only the status.
Update:
Ok, my goal is to post a status with a photo to a social network page.
For more information, read this:
http://developer.mixi.co.jp/en/connect/mixi_graph_api/mixi_io_spec_top/voice-api/#toc-10
This is my code. It works but post only the status, not photo.
$apiCon = curl_init(self::API_POST_STATUS_WITH_PHOTO);
curl_setopt($apiCon, CURLOPT_CONNECTTIMEOUT, 30);
curl_setopt($apiCon, CURLOPT_RETURNTRANSFER, true);
curl_setopt($apiCon, CURLOPT_POST, true);
curl_setopt($apiCon, CURLOPT_POSTFIELDS, array('status' => rawurlencode($status), 'photo' => $photoInBinary));
curl_setopt($apiCon, CURLOPT_HTTPHEADER, array('Host: api.mixi-platform.com', 'Authorization: OAuth ' . $accessToken, 'Content-Type: multipart/form-data'));
$result = curl_exec($apiCon);
curl_close($apiCon);
First try not encoding your status field (i.e., remove rawurlencode). This is a double-encode and possibly this is why your host is complaining.
(As an aside, you don't need to set the content-type header explicitly; CURL will do that.)
If this isn't enough, you either have to rearrange your request to use CURL's magic file upload mechanism, or you have to construct the entire multipart/form-data string by yourself.
This is how CURL's magic file mechanism works (from the documentation to CURLOPT_POSTFIELDS:
The full data to post in a HTTP "POST" operation. To post a file, prepend a filename with # and use the full path. The filetype can be explicitly specified by following the filename with the type in the format ';type=mimetype'. This parameter can either be passed as a urlencoded string like 'para1=val1&para2=val2&...' or as an array with the field name as key and field data as value. If value is an array, the Content-Type header will be set to multipart/form-data. As of PHP 5.2.0, value must be an array if files are passed to this option with the # prefix.
The only way to pass the content-type of a specific field in a multipart/form-data POST using CURL is with this syntax for the field value: #filepath;type=mime/type.
If your $photoInBinary started life in a file, simply pass the filename in the above format to CURL instead of opening the file and reading in the data.
However, if you created the photo yourself in memory, you need to write that to a temporary file. Below is how you might do that:
function curlFileUploadData($data, $type='') {
// $data can be a string or a stream
$filename = tempnam(sys_get_temp_dir(), 'curlupload-');
file_put_contents($filename, $data);
$curlparam = "#{$filename}";
if ($type) {
$curlparam .= ";type={$type}";
}
return array($curlparam, $filename);
}
list($curlparam, $tmpfile) = curlFileUploadData('thedata', 'image/bmp');
// You can see the raw bits CURL sends if you run 'nc -l 9999' at a command line first
$c = curl_init('http://localhost:9999');
curl_setopt($c, CURLOPT_POSTFIELDS, array('status' => 'good', 'photo' => $curlparam));
curl_exec($c);
curl_close($c);
unlink($tmpfile); // REMEMBER TO DO THIS!!!!
Note that CURL will set the filename parameter on the multipart file upload. Be sure the host doesn't use this for anything important. As far as I know there's no way to override the filename CURL sends--it will always be exactly what was given.
If you are not willing to create a temporary file and you must use CURL, you will have to create the entire multipart/form-data body as a string in memory, and give that to CURL as a string to CURL_POSTFIELDS, and manually set the content-type header to multipart/form-data. This is left as an exercise for the reader. By this time you should consider using the HTTP extension instead, or even fopen with stream_context_create() to set the http method and headers.

How to receive and handle an XML file requested through POST in PHP5?

I want to use simeplexml class in PHP5 to handle a small XML file. But to obtain that file, script has to send a specific POST request to a remote server that will "give" me an XML file in return. So I believe I can't use the "simplexml_load_file" method. This file is needed just for processing, then it can, or even should, be gone/deleted.
I've got HTTP HEADER of this type
$header = 'POST '.$gateway.' HTTP/1.0'."\r\n" .
'Host: '.$server."\r\n".
'Content-Type: application/x-www-form-urlencoded'."\r\n".
'Content-Length: '.strlen($param)."\r\n".
'Connection: close'."\r\n\r\n";
And not much idea of what to do next with that. There is fsockopen but I'm not sure if that would be appropriate or how to go with it.
My advice would be use something like Zend_Http_Client library or cURL. Getting everything right with fsockopen will be a pain to debug.
Zend_Http_Client has a nice interface and would work fabulously.
CURL isn't too much of a pain either and is already a part of most PHP builds.
Example below:
$ch = curl_init();
// set URL and other appropriate options
curl_setopt($ch, CURLOPT_URL, "http://www.example.com/"); // Replace with your URL
curl_setopt($ch, CURLOPT_POST, 1);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
$output = curl_exec($ch) // Return the XML string of data
// Parse output to Simple XML
// You'll probably want to do some validation here to validate that the returned output is XML
$xml = simplexml_load_string($output);
I'd use an HTTP client library like Zend_Http_Client (or cURL if you're a masochist) to create the POST request, then feed the response body into simplexml_load_string or SimpleXMLElement::__construct()

Categories