Getting bad request with CURL in PHP posting XML data - php

I am using PHP CURL to post some xml data to another server using that IP, i fullfilled all the requirements for making it work, but still getting an error of "BAD REQUEST".
Have a look on the code below.
$var2 ="<doc><item>Some content.</item></doc>";
$url = "server IP";
$header = "POST HTTP/1.1 \r\n";
$header .= "Content-type: text/xml \r\n";
$header .= "Content-length: ".strlen($var2)." \r\n";
$header .= "Content-transfer-encoding: text \r\n";
$header .= "Connection: close \r\n\r\n";
$header .= $var2;
$ch = curl_init();
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false);
curl_setopt($ch, CURLOPT_URL,$url);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
curl_setopt($ch, CURLOPT_TIMEOUT, 4);
curl_setopt($ch, CURLOPT_POSTFIELDS, $var2);
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, $header);
// Get Response
$data = curl_exec($ch);
if(curl_errno($ch))
print curl_error($ch);
else
curl_close($ch);
echo $data;

First, you are using this as request-line :
$header = "POST HTTP/1.1 \r\n";
According to the HTTP 1.1 RFC, the **Request-Line** should look like this :
Request-Line = Method SP Request-URI SP HTTP-Version CRLF
So, it seems you should add an URI between POST and HTTP/1.1
(But, before coding that, read what I've written in the next paragraphs of my answer)
Then, you are passing a lot of things as CURLOPT_CUSTOMREQUEST.
I don't think you can pass all those header using that option (quoting) :
Valid values are things like "GET", "POST", "CONNECT" and so on;
i.e. Do not enter a whole HTTP request line here. For
instance, entering "GET /index.html HTTP/1.0\r\n\r\n" would be
incorrect.
If you want to specify custom headers (it seems you do), you should use the CURLOPT_HTTPHEADER option, instead of CURLOPT_CUSTOMREQUEST (quoting the documentation of the first one) :
An array of HTTP header fields to set, in the format
array('Content-type: text/plain', 'Content-length: 100')

Related

PHP Curl Post with PayPal's NEW Header Requirement

I'm using the PHP sample code provided by Paypal for Paypal Payments Pro or Direct Pay. In a recent email from Paypal, they state
Starting October 7, 2013, we will require all incoming requests to have a “Host” header which complies with HTTP 1.1 Specifications. This header was not required under HTTP 1.0. IPN and PDT scripts using HTTP 1.0 may start failing with “HTTP/1.0 400 Bad Request” errors after October 7, 2013, which will result in IPN messages not being validated successfully, or PDT scripts not being able to retrieve transaction information.
They give me the following code to use as the header:
$header="POST /cgi-bin/webscr HTTP/1.1\r\n";
$header .="Content-Type: application/x-www-form-urlencoded\r\n";
$header .="Host: www.paypal.com\r\n";
$header .="Connection: close\r\n\r\n";
but using CURL, how can I pass all of that?
I know I can set most of that in an array and put it under the CURLOPT_HEADER but what about that FIRST line?
Here's my code. Please help me understand how to correctly add the new header request.
$curl = curl_init();
curl_setopt($curl, CURLOPT_VERBOSE, 1);
curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, FALSE);
curl_setopt($curl, CURLOPT_TIMEOUT, 30);
curl_setopt($curl, CURLOPT_URL, $api_endpoint);
curl_setopt($curl, CURLOPT_RETURNTRANSFER, 1);
curl_setopt($curl, CURLOPT_POSTFIELDS, $nvp_string);
$result = curl_exec($curl);
curl_close($curl);
Currently the headers are being stored in a string. To send custom headers with cURL, you can uset curl_setopt with CURLOPT_HTTPHEADER as follows:
curl_setopt($curl, CURLOPT_HTTPHEADER, array(
"POST /cgi-bin/webscr HTTP/1.1\r\n",
"Content-Type: application/x-www-form-urlencoded\r\n",
"Host: www.paypal.com\r\n",
"Connection: close\r\n\r\n"
)
);

Using curl_exec() in PHP, how do you create a custom HTTP header with an empty value?

This question is roughly equivalent to the issue addressed here:
http://curl.haxx.se/mail/lib-2010-08/0118.html
The point is that some web services (inconveniently) require POSTing
empty HTTP header values. So, for example, you may have a RESTful API
that requires this HTTP header for a POST:
Content-Type: text/json
Content-Length: 1024
...
Custome-Header-Field: hello
Required-Empty-Header-Field:
...
Connection: Keep-Alive
The point here is that Required-Empty-Header-Field must be specified, and must be empty.
How do you do this in curl_exec within a PHP context?
I am going to answer my own question, as this took me a while to figure out.
But I'm curious to see what other programmers have to say.
// Assume we are POSTing data from the variable $data.
$http_header = array(
"Content-Type: application/x-www-form-urlencoded" . "\r\n" .
"Content-Length: ". strlen($data) . "\r\n" .
...
"Custome-Header-Field: hello" . "\r\n" .
"Required-Empty-Header-Field:" . "\r\n" .
...
"Connection: Keep-Alive",
"Other-Value-One: world",
"Other-Value-Two: thisworks");
// Add optional headers.
if (!empty($some_condition)) {
array_push($http_header, "Other-Value-Three: whatever");
}
// Set up curl_exec.
$curl = curl_init();
$url = "https://www.somewhere-over-the-rainbow.com";
curl_setopt($curl, CURLOPT_URL,$url);
curl_setopt($curl, CURLOPT_FOLLOWLOCATION, 1);
curl_setopt($curl, CURLOPT_RETURNTRANSFER, 1);
curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, FALSE);
curl_setopt($curl, CURLOPT_POST, 1);
curl_setopt($curl, CURLOPT_POSTFIELDS, $data);
curl_setopt($curl, CURLOPT_HTTPHEADER, $httpHeader);
$response = curl_exec($curl);
curl_close($curl);
I haven't tested this, but try just setting a blank space for the value. This may get past any validation cURL is doing, and whitespace prior to a value in the headers is supposed to be ignored.

How to fix 302 Found CURL?

I am trying to send XML data through CURL with HTTP POST in PHP script. I am getting following message on execution.
Found
The document has moved here.
And here is my code.
<?php
$url = "https://usm.channelonline.com/REQUEST";
$post_string = '<export_documents_request schemaVersion="4.0">
<options>
<onlyUnexported>false</onlyUnexported>
<eventInRange>
<eventType>modified</eventType>
<after>2005-01-01T10:00:00Z</after>
</eventInRange>
</options>
</export_documents_request>';
$header = "POST HTTP/1.1 \r\n";
$header .= "Content-type: text/xml \r\n";
$header .= "Content-length: ".strlen($post_string)." \r\n";
$header .= "Content-transfer-encoding: text \r\n";
$header .= "Connection: close \r\n\r\n";
$header .= $post_string;
$ch = curl_init();
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, 0);
curl_setopt($ch,CURLOPT_FOLLOWLOCATION,TRUE);
curl_setopt($ch,CURLOPT_MAXREDIRS,10);
curl_setopt($ch, CURLOPT_URL,$url);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
curl_setopt($ch, CURLOPT_TIMEOUT, 4);
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, $header);
$data = curl_exec($ch);
if(curl_errno($ch))
print curl_error($ch);
else
echo $data;
curl_close($ch);
?>
And here is screen view on execution.
I am good in PHP but just familiar with CURL. Can you help me out how to fix this issue?
Either, like MrCode pointed out, the server doesn't really respond with 301/302 or your PHP is running in safe mode, which doesn't allow for using CURLOPT_FOLLOWLOCATION.

How to properly send and receive XML using curl?

I've been trying to post XML and get response from the server but with no luck.
Here are the conditions on server side:
Requests to the server should be sent as XML over HTTP 1.1.
The following requirements apply to the HTTP request:
The request type should be POST;
A Content-Length header should be present, and the total length of the request should be below 16KB;
A Content-Type header should be present, containing the media type value text/xml;
Here is my script:
$url = "http://somedomain.com";
$xml = '<?xml version="1.0" encoding="UTF-8"?>
<Request PartnerID="asasdsadsa" Type="TrackSearch"> <TrackSearch> <Title>love</Title> <Tags> <MainGenre>Blues</MainGenre> </Tags> <Page Number="1" Size="20"/> </TrackSearch> </Request>';
$header = "POST HTTP/1.1 \r\n";
$header .= "Content-type: text/xml \r\n";
$header .= "Content-length: ".strlen($xml)." \r\n";
$header .= "Connection: close \r\n\r\n";
$header .= $xml;
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL,$url);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
curl_setopt($ch, CURLOPT_TIMEOUT, 10);
curl_setopt($ch, CURLOPT_POST, true);
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, $header);
$data = curl_exec($ch);
echo $data;
if(curl_errno($ch))
print curl_error($ch);
else
curl_close($ch);
This gives me:
HTTP Error 400. The request URL is invalid.
Bad Request - Invalid URL
Does this help?
<?php
$url = "http://stackoverflow.com";
$xml = '<?xml version="1.0" encoding="UTF-8"?><Request PartnerID="asasdsadsa" Type="TrackSearch"> <TrackSearch> <Title>love</Title> <Tags> <MainGenre>Blues</MainGenre> </Tags> <Page Number="1" Size="20"/> </TrackSearch> </Request>';
$headers = array(
"Content-type: text/xml",
"Content-length: " . strlen($xml),
"Connection: close",
);
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL,$url);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
curl_setopt($ch, CURLOPT_TIMEOUT, 10);
curl_setopt($ch, CURLOPT_POST, true);
curl_setopt($ch, CURLOPT_POSTFIELDS, $xml);
curl_setopt($ch, CURLOPT_HTTPHEADER, $headers);
$data = curl_exec($ch);
echo $data;
if(curl_errno($ch))
print curl_error($ch);
else
curl_close($ch);
?>
From the documentation.
CURLOPT_CUSTOMREQUEST
A custom request method to use instead of "GET"
or "HEAD" when doing a HTTP request. This is useful for doing "DELETE"
or other, more obscure HTTP requests. Valid values are things like
"GET", "POST", "CONNECT" and so on; i.e. Do not enter a whole HTTP
request line here. For instance, entering "GET /index.html
HTTP/1.0\r\n\r\n" would be incorrect.
The parameter for CURLOPT_CUSTOMREQUEST should be simply POST and you can use CURLOPT_HTTPHEADER to set headers.

cURL,WireShark. Setting headers to post data and get xml

Here is the dump from WireShark:
POST /drm/drm_production_v2.php HTTP/1.1
content-length: 278
content-type: text/xml
User-Agent: UserAgent 1.0
Connection: Keep-Alive
Authorization: Basic dXNlcm5hbWU6cGFzc3dvcmQ=
host: www.example.com
<methodCall>
<methodName>aMethod</methodName>
<params>
<param>
<value>
<base64>dXNlcm5hbWU6cGFzc3dvcmQ=</base64>
</value>
</param>
<param>
<value>
<struct/>
</value>
</param>
</params>
</methodCall>
I have the xml saved into a seperate file. Here's what I am doing:
<?php
function httpsPost($Url, $xml_data, $headers)
{
// Initialisation
$ch=curl_init();
// Set parameters
curl_setopt($ch, CURLOPT_FORBID_REUSE, 1);
curl_setopt($ch, CURLOPT_FRESH_CONNECT, 1);
curl_setopt($ch, CURLOPT_HTTPHEADER, $headers);
curl_setopt($ch, CURLOPT_URL, $Url);
// Return a variable instead of posting it directly
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
curl_setopt($ch, CURLOPT_USERPWD,"username:password");
// Activate the POST method
curl_setopt($ch, CURLOPT_POST, 1) ;
curl_setopt($ch,CURLOPT_USERAGENT,"UserAgent 1.0");
// Request
curl_setopt($ch, CURLOPT_POSTFIELDS, $xml_data);
curl_setopt($ch, CURLOPT_TIMEOUT, 999);
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, 0);
curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, 0);
// execute the connexion
$result = curl_exec($ch);
// Close it
curl_close($ch);
return $result;
}
$str='username:password';
$auth=base64_encode($str);
$request_file = "./request.xml";
$fh = fopen($request_file, 'r');
$filesize=filesize($request_file);
echo $filesize;
$xml_data = fread($fh,$filesize);
fclose($fh);
$url = 'http://www.example.com';
$header = array();
$header[] = "POST /drm/drm_production_v2.php HTTP/1.1";
$header[] = "Content-type: text/xml";
$header[] = "content-length: ".$filesize. "\r\n";
$header[] = "User-Agent: UserAgent 1.0";
$header[] = "Connection: Keep-Alive";
$header[] = "Authorization: Basic ".$auth;
$header[] = "host: www.example.com";
$Response = httpsPost($url, $xml_data, $header);
echo $Response;
?>
It returns a 'Bad Request' from the server. Any suggestions?
My first guess is that the extra "\r\n" after the content-length header makes the server think that the post content starts there. I'd also change "content-length", "Content-type", and "host" to "Content-Length", "Contnet-Type", and "Host", just in case.
Edit: That, and Ronald Bouman's answer.
I think your argument to
curl_setopt($ch, CURLOPT_POSTFIELDS, $xml_data);
is not correct. The postfields option should be URL encoded name/value pairs. From the docs:
"
This 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
"
see http://php.net/manual/en/function.curl-setopt.php
There's the pcap2curl tool that allows one to convert a pcap file of HTTP requests into cURL.
But if you want to replay some web requests from your browser then you can do it without Wireshark and instead enter Web developer mode in the browser. You then go to the network requests view and right click on the request of interest then, in most modern browsers, there is an option to Copy as cURL after which you can then paste the resulting command into a terminal and rerun the captured command as you see fit using the curl tool. Some browsers (e.g. Mozilla) also offer the option to edit and resend from within the browser.

Categories