Using PHP to post an XML buffer to an HTTPS page - php

I am using php 5.2.9
I have an XML buffer which I need to post to some HTTPS URL.
What is the correct way of doing that?
I see various examples on the web, and none of them seem to be working for me:
Some define the cURL headers like so:
$headers = array(
"POST " . $page . " HTTP/1.0",
"Content-type: text/xml;charset=\"utf-8\"",
"Accept: text/xml",
"Cache-Control: no-cache",
"Pragma: no-cache",
"SOAPAction: \"run\"",
"Content-length: ".strlen($buffer),
);
Where $page holds the request on the server and $buffer contains the XML data.
The actual $buffer is sent as the value as:
curl_setopt($curl, CURLOPT_POSTFIELDS, $buffer);
But I don't see how this can work, as CURLOPT_POSTFIELDS expects its value to be an array and not a buffer.
Then I saw several ways of configuring the SSL aspects of the call:
curl_setopt($curl, CURLOPT_SSLVERSION,3);
curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, false);
curl_setopt($curl, CURLOPT_SSL_VERIFYHOST, 2);
Are all of these needed? I saw examples where the following was also set:
curl_setopt($curl, CURLOPT_HTTPAUTH, CURLAUTH_BASIC );
curl_setopt($curl, CURLOPT_USERPWD, "username:password");
Can someone post a complete working example that explains what needs to be done in order to post an XML buffer using cURL to an HTTP URL?
Thanks in advance

A few points:
Don't specify the request method with the other headers. Use CURLOPT_CUSTOMREQUEST for that.
To send the data you have two options. You can either implement a stream wrapper that reads from $buffer that you then open with fopen and give as a CURLOPT_INFILE option (of course, if the XML is on disk, you can open it directly with fopen), or, more simply, you define a CURLOPT_READFUNCTION callback.
The verify peer part is only necessary if you want to check the validity of the server's certificate (you ought to).
Basic authentication is necessary if the server requires basic authentication. Only you can know that.

Related

Error when trying to send file in CURL POST call

I am trying to make a call from my Laravel application to the Bullhorn API to convert a document to HTML, but it looks like the file isn't being attached to the call. Below is my code:
$data = curl_file_create("full/path/to/file.docx", 'application/vnd.openxmlformats-officedocument.wordprocessingml.document', 'testcv');
$ch = curl_init();
curl_setopt($ch, CURLINFO_HEADER_OUT, 1);
curl_setopt($ch, CURLOPT_URL, $url);
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, 0);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
curl_setopt($ch, CURLOPT_VERBOSE, 1);
curl_setopt($ch, CURLOPT_POST, 1);
curl_setopt($ch, CURLOPT_POSTFIELDS, http_build_query($data));
$response = curl_exec($ch);
However I receive the following 500 error:
Bad File Uploaded: the request doesn't contain a multipart/form-data or multipart/mixed stream, content type header is application/x-www-form-urlencoded
When I try to set the Content-Type explicitly:
curl_setopt($ch, CURLOPT_HTTPHEADER, array(
'Content-Type: multipart/form-data'
));
I still receive a 500 error, just slightly different:
Bad File Uploaded: the request was rejected because no multipart boundary was found
I don't believe it's an issue with the Bullhorn API because executing the following call through the command line works fine:
curl -X POST "https://restXXX.bullhornstaffing.com/rest-services/{corptoken}/resume/convertToHtml?format=docx&BhRestToken={bhRestToken}" -F "file=#full\path\to\file.docx"
I assume that for some reason the file isn't being attached in my PHP call but I cannot figure out why.
Any help would be greatly appreciated.
The content-type: multipart/form-data uses boundary parameter for encapsulation, you can pass the boundary value as a string
'Content-Type: multipart/form-data; boundary=---------------------------7da24f2e50046'
Boundary is a string of "--" followed by a random string.
https://www.w3.org/Protocols/rfc1341/7_2_Multipart.html
If I ever have any weird problems with requests, I always put the request into postman and test it there since it compiles most of the headers and options for you, then just hit the code button on the top right to generate code in whatever language you want.
For your case you could datadump (dd) out your $data variable and copy it into one of the postman fields then set the url as well as the request type then run it and see what you get back.
Just a suggestion on how to debug a problem like this, hope it helps! good luck!

How do I make this Salesforce cURL request in PHP?

I'm using PHP to make cURL requests to Salesforce's REST API.
I've got most of the requests I need to make figured out, but I'm not sure how to convert the following curl command on the following Salesforce API page to a cURL request in PHP:
curl https://yourInstance.salesforce.com/services/data/v20.0/sobjects/Account/customExtIdField__c/11999 -H "Authorization: Bearer token" -H "Content-Type: application/json" -d #newrecord.json -X PATCH
https://developer.salesforce.com/docs/atlas.en-us.api_rest.meta/api_rest/dome_upsert.htm
I know that that -H option is for headers, which I'm handing with the following:
curl_setopt($ch, CURLOPT_HTTPHEADER, $headers);
And I think that the -X PATCH part can be accomplished with the following PHP cURL option:
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'PATCH');
However, how do I handle the -d #newrecord.json part in PHP cURL?
Thanks.
You should POST the json
$post = json_encode($data);
curl_setopt($ch, CURLOPT_POSTFIELDS,$post);
What you are doing with -d #newrecord.json is uploading a (JSON) file for the endpoint to use. To replicate this in PHP, you need to pass an array with a file element to CUROPT_POSTFIELDS, like this:
$file = [
"file" => "#newrecord.json";
];
curl_setopt($ch, CURLOPT_POSTFIELDS, $file);
Make sure to give the correct file path. You can use realpath() to aid with this.
Alternatively, you could just send the JSON encoded data:
$data = [
"site" => "Stack Overflow",
"help" => true,
];
$jsonData = json_encode($data);
curl_setopt($ch, CURLOPT_POSTFIELDS, $jsonData);
Don't forget to set your Content-Type: application/json header!
Lastly, your guess about the PATCH request is correct:
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'PATCH');

Encoding password field for HTTP Basic Auth

I using PHP cURL to communicate with a Rest API. Most of the functionality is carried out using X-Ephemeral-Tokens, but unfortunately they don't allow delete permissions to be given through these, so I am having to implement a function to delete through HTTP Basic Authentication.
The trouble I'm having is that the password for the testing account is a random string, including multiple special characters (double quotation marks being some of them). I got the request working using the normal cURL binary by surrounding the username:password combination in single quotes (i.e. ') but I'm not sure how to convert this to PHP. The relevant snippet is below.
$curl = curl_init();
curl_setopt($curl, CURLOPT_HTTPHEADER, array("Accept: application/json",
"Content-Type: application/json"));
curl_setopt($curl, CURLOPT_HTTPAUTH, CURLAUTH_BASIC);
curl_setopt($curl, CURLOPT_USERPWD, $uname . ":" . $pass);
curl_setopt($curl, CURLOPT_URL, "https://cloud.ravellosystems.com/api/v1/applications/" . $appid);
curl_setopt($curl, CURLOPT_RETURNTRANSFER, 1);
curl_setopt($curl, CURLOPT_CUSTOMREQUEST, "DELETE");
$result = curl_exec($curl);
I've tried various combinations of quotation marks and URL encoding but I still get a response code which indicates the authentication isn't working right.
This is and exmaple HTTP request based on what the API documentation shows for using normal cURL (amended slightly)
curl -v -X DELETE -H "Content-Type: application/json" -H "Accept: application/json" --user ravello#ravello.com:password https://cloud.ravellosystems.com/api/v1/applications/414244
Any suggestions on how to get around this greatly appreciated.
Ok so if anyone else has this issue, I've found a solution.
Basically you can see in my code above that I was trying to use the cURL PHP methods to set options for CURLOPT_USERPWD and CURLOPT_HTTPAUTH. However somewhere in this the special characters in the password were causing issues with the parsing and I think only a section of it was actually being taken by the server.
However, all that these options do basically is set an HTTP header, as can be seen in #mpyw screenshots. This is the Authorization header, which is in the format below:
`Authorization: Basic [base64 encoded username:password]`
So I got rid of these options and did it manually, by base64 encoding my username:password string myself and then adding a header.
$auth = base64_encode($uname . ":" . $pass);
curl_setopt($curl, CURLOPT_HTTPHEADER, array("Authorization: Basic $auth",
"Accept: application/json",
"Content-Type: application/json"));
Now it all works fine! Thanks to the guys who posted answers - both of you contributed to me coming up with the workaround.
I compared two ways, enabling verbose outputs.
Emitted headers are the same, except that the PHP does not contain User-Agent header. Does the API require User-Agent header?
Edit
Password that contains '
Password that contains "
I believe that you have to base64_encode the username and password. Try this:
curl_setopt($curl, CURLOPT_USERPWD, base64_encode($uname . ":" . $pass));

php curl and CURLOPT_FOLLOWLOCATION behavior

Here's the situation. On one side, we have a server, with a RESTful service. One possible query to it is a POST query to create an object. When that is done, the server returns a 'Location' header, to indicate where information on the newly created object can be found.
However, said server is anal about having the correct Content-Type for each request. For instance, POST requires 'application/json', and GET requires this to be unset (make sense, since GET doesn't have a body).
To sum up, we have:
www.example.com/articles/ ; one can send a POST request with 'Content-Type: application/json', and server will return 'Location: www.example.com/articles/123' if 123 is the id of the new object ;
www.example.com/articles/123 ; one can send a GET request with no 'Content-Type' and server will return a description of the new article object.
On client side, we use PHP with cURL. We use the CURLOPT_FOLLOWLOCATIONsetting so we can read the description of the newly created object. Obviously, we also set 'Content-Type: application/json' for our POST request:
<?php
$ch = curl_init();
curl_setopt($ch, CURLOPT_POSTFIELDS, '{"name": "test"}');
curl_setopt($ch, CURLOPT_POST, 1);
curl_setopt($ch, CURLOPT_FOLLOWLOCATION, true);
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, 0);
curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, 0);
curl_setopt($ch, CURLOPT_URL, "https://www.example.com/Articles/");
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
curl_setopt($ch, CURLOPT_HTTPHEADER, ['Content-Type: application/json']);
$result=curl_exec($ch);
var_dump($result);
curl_close ($ch);
?>
This is what we get:
string(101) "{ "errorNo": 415, "error": "Unsupported Media Type Content-Type should not be set in a GET request" }"
I looked at the log of the server, and indeed, 'Content-Type: application/json' is sent to GET www.example.com/articles/123.
Is this an expected behaviour?
If yes, what is then best approach:
remove the 'Content-Type' check on GET requests, server-side?
(sounds silly)
forget about CURLOPT_FOLLOWLOCATION, and make 2 clearly separated curl requests, so I have control over the headers? (but then what's the point of CURLOPT_FOLLOWLOCATION?)
something else?
For control and testing, I also use Postman, and I have no problem with it, it follows the location, doesn't send the 'Content-Type' on the GET part (apparently) after the redirection and so I don't have an error.
EDIT:
There seems to be nothing useful in the PHP doc. But I found something interesting in the command line man page:
https://curl.haxx.se/docs/manpage.html
It says:
"WARNING: headers set with this option will be set in all requests - even after redirects are followed, like when told with -L, --location."
So I guess it probably is the expected behaviour for PHP too. May someone suggest best practices then?
Have you tried using
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, "POST");
to set the post type

cURL with PUT method

Is it possible to do a cURL request with the PUT method using only a URL? Here is the URL i would like to be able to call using cURL with the PUT method:
$url = https://url.net/card/activate.xml?card_id=1234567890&application_key=123123&accesskey=abcdef
I was able to make it work using the REST Console(Chrome Extension) but not using cURL. I tried using curl_setopt with CURLOPT_PUT, CURLOPT_INFILE and CURLOPT_INFILESIZE but I was receiving an empty page, no response at all.
This is the headers and curl options I currently have:
$header[] = 'Authorization: Basic abc123';
$header[] = 'Accept: text/xml';
curl_setopt($curl, CURLOPT_CUSTOMREQUEST, 'PUT');
curl_setopt($curl, CURLOPT_URL, $url);
curl_setopt($curl, CURLOPT_HTTPHEADER, $header);
curl_setopt($curl, CURLOPT_SSL_VERIFYHOST, false);
curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, false);
curl_setopt($curl, CURLOPT_RETURNTRANSFER, true);
$output = curl_exec($soap_do);
Currently, with those settings, I'm getting the 411 Length Required error. Would it be possible to make it work like that or am I trying to configure something that will never work?
The HTTP 411 Error
The Web server (running the Web site) thinks that the HTTP data stream sent by the client (e.g. your Web browser or our CheckUpDown robot) should include a 'Content-Length' specification. This is typically used only for HTTP methods that result in the placement of data on the Web server, not the retrieval of data from it.
You need to set the Content Length of your Data
$header[] = 'Content-length: '.strlen($put_data);
OR If you don't have any data, just put 0 there.

Categories