We use cookies to improve your experience. No personal information is gathered and we don't serve ads. Cookies Policy.

ExpressionEngine Logo ExpressionEngine
Features Pricing Support Find A Developer
Partners Upgrades
Blog Add-Ons Learn
Docs Forums University
Log In or Sign Up
Log In Sign Up
ExpressionEngine Logo
Features Pro new Support Find A Developer
Partners Upgrades
Blog Add-Ons Learn
Docs Forums University Blog
  • Home
  • Forums

curl to EE URI returning 400 error

Development and Programming

Paul_B's avatar
Paul_B
86 posts
15 years ago
Paul_B's avatar Paul_B

I’m developing a new piece of functionality for a client so that he can sync up his accounts software with his site (EE powered, with a cartthrob shopping basket)

His software (Sage) grabs an XML feed from a URL and then once it’s got it, it returns an XML confirmation of the orders it’s picked up.

It does this all from one URL, so I’m writing a script to check if there is any xml to parse, otherwise output an xml report.

I’m trying to test this script by posting some xml to my EE template via CURL. I’ve got full the address of the template correct, but for some reason it’s returning a 400 error:

Bad Request Your browser sent a request that this server could not understand.

Invalid URI in request POST HTTP/1.0

Resource id #49

My test script looks like this (the XML formatting is ok, I’ve validated it)

<?php
$post_string = '<?xml version="1.0" encoding="UTF-8"?>
<Company>
    <Customers>
             <Customer>
                       <Id>1</Id>
                       <AccountReference>ABC</AccountReference>
             </Customer>
             <Customer>
                        <Id>2</Id>
                        <AccountReference>DEF</AccountReference>
             </Customer>
        </Customers>
    <SalesOrders>
        <SalesOrder>
            <Id>3</Id>
            <SalesOrderNumber>17</SalesOrderNumber>
             </SalesOrder>
        <SalesOrder>
            <Id>17</Id>
            <SalesOrderNumber>57</SalesOrderNumber>
            </SalesOrder>
    </SalesOrders>
    <Invoices>
                <Invoice>
                 <Id>1</Id>
             <InvoiceNumber>56</InvoiceNumber>
             </Invoice>
        <Invoice>
             <Id>2</Id>
              <InvoiceNumber>57</InvoiceNumber>
            </Invoice>
    </Invoices>
    <Transactions>
             <Transaction>
            <Id>1</Id>
            <TransactionNumber>56</TransactionNumber>
             </Transaction>
        <Transaction>
            <Id>2</Id>
            <TransactionNumber>57</TransactionNumber>
            </Transaction>
    </Transactions>
</Company>';

$header  = "POST HTTP/1.0 \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_URL,"http://www.mydomain.com/reports/download/"); 
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
curl_setopt($ch, CURLOPT_TIMEOUT, 4);
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, $header);

$data = curl_exec($ch); 
echo $data . "\r\n";

if(curl_errno($ch)) {
    print curl_error($ch); 
}
else {
    print($ch);
    curl_close($ch);
}
?>

This is posting to this template:

<?php 
    $xmlInput = file_get_contents("php://input");
    // Get rid of attributes in <Company> tag as xml2array doesn't like them for some reason?
    $xmlString = preg_replace('/<Company.*?>/', '<Company>', $xmlInput, 1);   
    
    // Check to see if xml has been posted to this script (Notification)
    if (!empty($xmlString))
    {
    
        // Convert xml string to array
        $xmlData = xml2array($xmlString);           
        
        if (!$xmlData)
        {
            die("Couldn't read XML data");
        }
        
        // Extract SalesOrder Nodes
        $salesOrderNodes =& $xmlData['Company'][0]['SalesOrders'][0]['SalesOrder'];
        
        // Loop through sales order nodes
        foreach($salesOrderNodes as $node)
        {
            $orderID = $node['Id'];
            $results = $DB->query("SELECT title FROM exp_titles WHERE entry_id = ".$orderID." AND status NOT IN ('Closed','Failed','Processing')");
            $data = array('status' => 'Sent to accounts');
            if ($results->num_rows > 0)
            {
                $sql = $this->EE->db->update_string('exp_titles', $data, 'entry_id = $orderID');
                $this->EE->db->query($sql);
            }
        }
    }
    else
    {
        echo "{embed=reports/latest}";
    }
?>

I realise this is a very specific issue - I’m really wondering if it’s the EE URI that’s causing the problem. I can put the address into my browser fine, but when I use it in CURL it returns the error. I haven’t tried doing this before, so I might be doing something foolish 😊

[Mod Edit: Moved to the Development & Programming forum]

       
Greg Salt's avatar
Greg Salt
3,988 posts
15 years ago
Greg Salt's avatar Greg Salt

Hi Paul,

I’m going to move this thread to the Development & Programming forum since it’s more appropriate there. Have you tried just posting the data without adding the headers?

Cheers

Greg

       
Paul_B's avatar
Paul_B
86 posts
15 years ago
Paul_B's avatar Paul_B

Hi Greg

I’ve tried not posting the headers and only posting the XML. I still get a 400 error, but the message is now

“The request line contained invalid characters following the protocol string.”

If I only declare the POST header and the xml, the error returns to the invalid URI message.

However, if I remove all of the actual postdata and allow the curl to execute without it, it returns the download template, so the URI must be fine?

Any ideas very welcome, I’m a bit stumped! 😊

       
Manuel Payano's avatar
Manuel Payano
144 posts
15 years ago
Manuel Payano's avatar Manuel Payano

Sometimes mod_security can cause this Try adding this to your htaccess file

<IfModule mod_security.c>
SecFilterEngine Off
SecFilterScanPOST Off
</IfModule>

<IfModule mod_security2.c>
SecRuleEngine off
SecFilterScanPOST Off
</IfModule>
       
Paul_B's avatar
Paul_B
86 posts
15 years ago
Paul_B's avatar Paul_B

Hi

Thanks for that .htaccess update - unfortunately it hasn’t made the problem go away :(

I’ve made some changes to my scripts in a bid to get it working. The test_script that posts the xml to the handler URI now looks like this:

<?php
$post_string = <<<XML
<Company>
    <Customers>
        <Customer>
            <Id>1</Id>
            <AccountReference>ABC</AccountReference>
        </Customer>
        <Customer>
            <Id>2</Id>
            <AccountReference>DEF</AccountReference>
        </Customer>
    </Customers>
    <SalesOrders>
        <SalesOrder>
            <Id>3</Id>
            <SalesOrderNumber>17</SalesOrderNumber>
        </SalesOrder>
        <SalesOrder>
            <Id>17</Id>
            <SalesOrderNumber>57</SalesOrderNumber>
        </SalesOrder>
    </SalesOrders>
    <Invoices>
        <Invoice>
            <Id>1</Id>
            <InvoiceNumber>56</InvoiceNumber>
        </Invoice>
        <Invoice>
            <Id>2</Id>
            <InvoiceNumber>57</InvoiceNumber>
        </Invoice>
    </Invoices>
    <Transactions>
        <Transaction>
            <Id>1</Id>
            <TransactionNumber>56</TransactionNumber>
        </Transaction>
        <Transaction>
            <Id>2</Id>
            <TransactionNumber>57</TransactionNumber>
        </Transaction>
    </Transactions>
</Company>
XML;
$xml = simplexml_load_string($post_string);
libxml_use_internal_errors(true);
$sxe = simplexml_load_string($post_string);
if (!$sxe) {
    echo "Failed loading XML<br>";
    foreach(libxml_get_errors() as $error) {
    echo "<br>", $error->message;
    }
    libxml_clear_errors();
}
$headers = array( 
    "POST HTTP/1.0", 
    "Content-type: text/xml", 
    "Content-length: ".strlen($post_string), 
    "Content-transfer-encoding: text", 
    ); 

$uploadURL = "http://www.mydomain.com/reports/download";

$ch = curl_init();    /* Create a CURL handle. */ 

/* Set cURL options. */ 
curl_setopt($ch, CURLOPT_URL, $uploadURL); 
curl_setopt($ch, CURLOPT_POST, true); 
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); 
curl_setopt($ch, CURLOPT_FAILONERROR, true); 
curl_setopt($ch, CURLOPT_HTTPHEADER, $headers ); 
curl_setopt($ch, CURLOPT_POSTFIELDS,$post_string); 
$result = curl_exec($ch);  /* Execute the HTTP request. */
$info = curl_getinfo($ch);
/*output anything that might be useful*/
print_r($result);
print_r($info);
if(curl_error($ch)) {
    print curl_error($ch); 
} else {
    print($ch);
    curl_close($ch);
}
?>

This outputs the following (presumably from the print_r($info) line:

Array ( [url] => http://www.mydomain.com/reports/download [content_type] => text/html [http_code] => 200 [header_size] => 423 [request_size] => 972 [filetime] => -1 [ssl_verify_result] => 0 [redirect_count] => 0 [total_time] => 0.186161 [namelookup_time] => 0.049204 [connect_time] => 0.049373 [pretransfer_time] => 0.049453 [size_upload] => 0 [size_download] => 0 [speed_download] => 0 [speed_upload] => 0 [download_content_length] => 0 [upload_content_length] => 0 [starttransfer_time] => 0.186114 [redirect_time] => 0 ) Resource id #49

I’m not sure what the Resource id #49 thing is, googling it doesn’t reveal very much. It doesn’t generate a 400 error any more, which is something.

Running out of characters in this post - continued in next post!

       
Paul_B's avatar
Paul_B
86 posts
15 years ago
Paul_B's avatar Paul_B

Second part of post!

The handler script (called ‘download’) now looks like this (new xml2array function)

<?php 
function xml2array ($xml_data)
    {
        // parse the XML datastring
        $xml_parser = xml_parser_create ();
        xml_parser_set_option($xml_parser, XML_OPTION_CASE_FOLDING, 0);
        xml_parser_set_option($xml_parser, XML_OPTION_SKIP_WHITE, 1);
        xml_parse_into_struct ($xml_parser, $xml_data, $vals, $index);
        xml_parser_free ($xml_parser);
        
        // convert the parsed data into a PHP datatype
        $params = array();
        $ptrs[0] = & $params; 
        foreach ($vals as $xml_elem) {
           $level = $xml_elem['level'] - 1;
           switch ($xml_elem['type']) {
           case 'open':
                  $tag_or_id = (array_key_exists ('attributes', $xml_elem)) ? $xml_elem['attributes']['ID'] : $xml_elem['tag'];
                   $ptrs[$level][$tag_or_id][] = array ();
                $ptrs[$level+1] = & $ptrs[$level][$tag_or_id][count($ptrs[$level][$tag_or_id])-1];
                break;
           case 'complete':
                   $ptrs[$level][$xml_elem['tag']] = (isset ($xml_elem['value'])) ? $xml_elem['value'] : '';
                   break;
           }
        }
        return ($params);
    }

    $xmlInput = file_get_contents("php://input");
    // Get rid of attributes in <Company> tag as xml2array doesn't like them for some reason?
    $xmlString = preg_replace('/<Company.*?>/', '<Company>', $xmlInput, 1);   
    // Check to see if xml has been posted to this script (Notification)
    if (!empty($xmlString))
    {
        // Convert xml string to array
        $xmlData = xml2array($xmlString);     
        print_r($xmlData);      
        
        if (!$xmlData)
        {
            die("Couldn't read XML data");
        }
        
        // Extract SalesOrder Nodes
        $salesOrderNodes =& $xmlData['Company'][0]['SalesOrders'][0]['SalesOrder'];
        
        // Loop through sales order nodes
        foreach($salesOrderNodes as $node)
        {
            $orderID = $node['Id'];
            $results = $DB->query("SELECT title FROM exp_titles WHERE entry_id = ".$orderID." AND status NOT IN ('Closed','Failed','Processing')");
            $data = array('status' => 'Sent to accounts');
            if ($results->num_rows > 0)
            {
                echo "Updating record ".$orderID."<br>";
                $sql = $this->EE->db->update_string('exp_titles', $data, 'entry_id = $orderID');
                $this->EE->db->query($sql);
            }
            else {
                echo "No records to update";
            }
        }
    }
    else
    {
        echo "{embed=reports/latest}";
    }
?>

Because the xml stuff happens via cURL I can’t see if the what the download/update script is doing (or not doing). I can see that no db updates are happening, but for all I know it’s falling over on the first line. I’ve tried POSTing an xml file to this URI instead of doing it via cURL but that seemed to open up a whole new raft of problems.

If anyone out there has managed to wade through all this and can offer me any ideas as to how to get this working I’ll be extremely gratefully, and might be able to save the what’s left of the hair that I haven’t torn out yet today…

       

Reply

Sign In To Reply

ExpressionEngine Home Features Pro Contact Version Support
Learn Docs University Forums
Resources Support Add-Ons Partners Blog
Privacy Terms Trademark Use License

Packet Tide owns and develops ExpressionEngine. © Packet Tide, All Rights Reserved.