I tested Magento's SOAP API using soapUI. I successfully logged in and got a login hash. Then I tried to retrieve a list of products and this worked fine.
This was on a Linux server using the latest version of Apache, mySQL and PHP.
I then created a backup of Magento and the database. I wanted to create a test environment on a Lion server using a MAMP stack. The Magento backup seems to work fine, but the SOAP API doesn't.
Again I used soapUI to get a login hash and tried to retrieve all products. But now the response seems to be incomplete:
<?xml version="1.0" encoding="UTF-8"?>
<SOAP-ENV:Envelope xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/" xmlns:ns1="urn:Magento">
<SOAP-ENV:Body>
<ns1:catalogProductListResponseParam>
<result>
<complexObjectArray>
<product_id>7167</product_id>
<sku>000140</sku>
... etc ...
<complexObjectArray>34</complexObjectArray>
</category_ids>
<website_ids>
<complexObjectArray>1</complexObjectArray>
</website_ids>
</complexObjectArray>
<complexObjectArray>
<product
Why is the reponse incomplete under Lion/MAMP?
I had a problem with incomplete SOAP XML responses once.
The setup was like this:
Even if you use a different Magento version and a different SOAP call, you may suffer from a similar bug as I did. The problem was that the HTTP Content-Length header was calculated incorrectly for responses > 8000 bytes.
How to verify if you are affected by this bug:
As the second way is the more accurate way to do it, I'll go with this.
Write the result of the search/replace operations into a variable and log it for further examination. The code may look like this:
public function run()
{
$apiConfigCharset = Mage::getStoreConfig("api/config/charset");
if ($this->getController()->getRequest()->getParam('wsdl') !== null) {
/* don't modify the first part ... */
} else {
try {
$this->_instantiateServer();
$content = preg_replace(
'/(\>\<)/i',
">\n<",
str_replace(
'<soap:operation soapAction=""></soap:operation>',
"<soap:operation soapAction=\"\" />\n",
str_replace(
'<soap:body use="literal"></soap:body>',
"<soap:body use=\"literal\" />\n",
preg_replace(
'/<\?xml version="([^\"]+)"([^\>]+)>/i',
'<?xml version="$1" encoding="'.$apiConfigCharset.'"?>',
$this->_soap->handle()
)
)
)
);
Mage::log($content, null, 'soap.log');
$this->getController()->getResponse()
->clearHeaders()
->setHeader('Content-Type','text/xml; charset='.$apiConfigCharset)
->setBody($content);
} catch( Zend_Soap_Server_Exception $e ) {
$this->fault( $e->getCode(), $e->getMessage() );
} catch( Exception $e ) {
$this->fault( $e->getCode(), $e->getMessage() );
}
}
}
Execute the SOAP API call and open var/log/soap.log in your Magento directory. If the XML is complete in the log file but not in your response, then the Content-Length header is the problem.
How to correct the Content-Length header
Stay in your copy of Mage_Api_Model_Server_Wsi_Adapter_Soap and add one line to the code we just modified:
public function run()
{
$apiConfigCharset = Mage::getStoreConfig("api/config/charset");
if ($this->getController()->getRequest()->getParam('wsdl') !== null) {
/* don't modify the first part ... */
} else {
try {
$this->_instantiateServer();
$content = preg_replace(
'/(\>\<)/i',
">\n<",
str_replace(
'<soap:operation soapAction=""></soap:operation>',
"<soap:operation soapAction=\"\" />\n",
str_replace(
'<soap:body use="literal"></soap:body>',
"<soap:body use=\"literal\" />\n",
preg_replace(
'/<\?xml version="([^\"]+)"([^\>]+)>/i',
'<?xml version="$1" encoding="'.$apiConfigCharset.'"?>',
$this->_soap->handle()
)
)
)
);
Mage::log($content, null, 'soap.log');
$this->getController()->getResponse()
->clearHeaders()
->setHeader('Content-Type','text/xml; charset='.$apiConfigCharset)
->setHeader('Content-Length',strlen($content), true)
->setBody($content);
} catch( Zend_Soap_Server_Exception $e ) {
$this->fault( $e->getCode(), $e->getMessage() );
} catch( Exception $e ) {
$this->fault( $e->getCode(), $e->getMessage() );
}
}
}
Note the line:
->setHeader('Content-Length',strlen($content), true)
We calculate and set the Content-Length header. The third parameter, true, is important because it tells the framework to overwrite the existing Content-Length header.
If you ask why this problem occurs I can't tell you exactly because I didn't hunt the bug all the way down.
From what I saw, everything is fine as long as the XML response has a length <= 8000 bytes. If the response is longer than 8000 bytes and the XML consists of x lines, the response is truncated by x characters. It looks like it is a problem with the different carriage return codes and/or with encoding issues which lead to the wrong calculation of the response content length.