ZF-2978: Zend_XmlRpc_Client returning system.methodSignature in methodName

Description

Hi,

I have just upgraded from 1.5 to 1.5.1 and now my XML-RPC client calls no longer work. My tracing show that system.methodSignature is being put in methodName.

The traces below reflect no change in my own code, only an upgrade to 1.5.1!

1.5 trace


<?xml version="1.0" encoding="UTF-8"?>
getVersionVPRabcdefg

1.5.1 trace


<?xml version="1.0" encoding="UTF-8"?>
system.methodSignaturegetVersion

As stated, all I have done is upgrade ZF to 1.5.1, nothing else has changed ... promise!

Comments

Can you provide the minimum code to reproduce this issue? I've tried the following:

Server: has method 'foo.say' accepting a single string "$message".

Client code:


$conn   = new Zend_XmlRpc_Client('http://localhost/xml-rpc');
$client = $conn->getProxy();

try {
    $result = $client->foo->say('hello');
    echo $result, "\n\n";
} catch (Exception $e) {
    echo "An exception occurred: " . $e->getMessage() . "\n\n";
}
echo "Last Request: ", $conn->getLastRequest()->__toString();

Expected and Actual Result:


hello

Last Request: <?xml version="1.0" encoding="UTF-8"?>
foo.sayhello

As you'll note, the request object clearly uses the 'foo.say' method as expected, and I get back a valid response. If you could provide some basic code showing how you reproduce the issue, I may be able to help you out better; right now, I can't identify any issues.

Sure,

I have my own XML-RPC client class that has all my calls in e.g.


    public function xselect($p1, $p2)
    {
        $methodType = 'foo.xselect';
        $params = array($p1, $p2);
        return $this->xmlRpcRequest($methodType, $params);
    }

    /**
     * Main XmlRpc call
     *
     * @param string $methodType
     * @param array $params
     * @return array
     * @throws Zend_Exception if call fails
     */
    public function xmlRpcRequest($methodType, $params)
    {
        if (isset($this->_host)) {
            $host = $this->_host;
        } 

        if (isset($this->_port)) {
            $port = $this->_port;
        }

        $client = new Zend_XmlRpc_Client('http://' . $host. ':' . $port);

        try {
            return $client->call($methodType, $params);
        } catch (Zend_Exception $e) {
            Zend_Registry::get('logger')->err($e->getMessage());
            return array('Error' => $e->getCode(), 'Message' => $e->getMessage());
        }

I then use the above in code like so:


$client = new My_XmlRpc();

$host = $params->getParam('host');
$port = $params->getParam('port');

$client->_host = $host;
$client->_port = $port;
$result = $client->xselect('A', 'B');

The above works just fine with 1.5.0, but not with 1.5.1. Have I missed some changes in the way the client is used?

Cheers!

I've now tried the following code, which uses Zend_XmlRpc_Client::call(), as per your example:


$conn   = new Zend_XmlRpc_Client('http://framework/xml-rpc');

try {
    $result = $conn->call('foo.say', array('hello'));
    echo $result, "\n\n";
} catch (Exception $e) {
    echo "An exception occurred: " . $e->getMessage() . "\n\n";
}
echo "Last Request: ", $conn->getLastRequest()->__toString();

Again, I get the same expected and actual results as previously posted.

system.methodSignature does get called during the course of the XML-RPC client setup, as we do server introspection to ensure that the parameters you pass to the method match those specified in the server method signature -- but this has been true since before 1.0.0.

1.5.1 did feature a number of bug-fixes to the XmlRpc component, but all changes were bugfixes and introduced no BC changes (which was verified by the fact that the test suite continued to pass all previous tests).

Can you please attempt to run the client using a straight XmlRpc client, no proxying via a custom object, to make sure it's not an issue with your wrapper class?

Hi Matthew,

OK, I did as you said and stripped it right back and I get the same results. It should be noted that the XML-RPC server I talk to is a windows service written in C using the Epinions API (http://xmlrpc-epi.sourceforge.net/main.php/…). This hasn't changed and works with requests coming from 1.5.0.

My code now looks like:


        $conn  = new Zend_XmlRpc_Client('http://localhost:8082');

        try {
            $result = $conn->call('OMAccs.ListReports');
            echo $result, "\n\n";
        } catch (Zend_XmlRpc_Exception $e) {
            echo $e->getMessage() . "\n\n";
        }
        echo $conn->getLastRequest()->__toString();

The response is:



Invalid response // Response from XML-RPC service

<?xml version="1.0" encoding="UTF-8"?>
system.methodSignatureOMAccs.ListReports

Comparing Zend_XmlRpc_Cient in 1.5.0 and 1.5.1 there is a new chunk of code in the 'call' function (lines 254-278), if I comment this code out I get a working client again and my response using the above call is:


C:\ColumbusOM\ZoneServer\queue\accounting\templates\devices.xml  // XML-RPC server response

<?xml version="1.0" encoding="UTF-8"?>
OMAccs.ListReports

Hi Matthew,

OK, I have taken this a step further and found a public XML-RPC server over at http://scripts.incutio.com (Simon Willison)


        $conn  = new Zend_XmlRpc_Client('http://scripts.incutio.com/xmlrpc/simpleserver.php');

        try {
            $result = $conn->call('test.getTime');
            echo $result, "\n\n";
        } catch (Zend_XmlRpc_Exception $e) {
            echo $e->getMessage() . "\n\n";
        }
        echo $conn->getLastRequest()->__toString();

Using 1.5.1 this server has the same problem with the above client request:


server error. requested method system.methodSignature does not exist.

<?xml version="1.0" encoding="UTF-8"?>
system.methodSignaturetest.getTime

If I comment out lines 254-278 in Zend_XmlRpc_Client it works!

Hmm... This is not good. The code added in call() was to make sure that empty arrays are sent as the appropriate XML-RPC type as specified by the server; in PHP, you can't tell if an empty array is associative or indexed, so we need to check against the signatures on the server.

The check is done using system.methodSignature(); well-formed XML-RPC servers should implement the various system.* methods to allow introspection such as this.

However, when they don't Zend_XmlRpc_Client should do the right thing. I think we have two options:

add a flag to Zend_XmlRpc_Client such as 'disableStrictSignatures' that would bypass this check

do some error handling in Zend_XmlRpc_Client_ServerIntrospection that would turn such a flag on by default if unable to call the method

I'll work on these for the 1.5.2 release. In the meantime, you can create an extension of the Zend_XmlRpc_Client class that overrides call() as follows:


class My_XmlRpc_Client extends Zend_XmlRpc_Client
{
    public function call($method, $params = array())
    {
        $request = new Zend_XmlRpc_Request($method, $params);

        $this->doRequest($request);

        if ($this->_lastResponse->isFault()) {
            $fault = $this->_lastResponse->getFault();
            throw new Zend_XmlRpc_Client_FaultException($fault->getMessage(),
                                                        $fault->getCode());
        }

        return $this->_lastResponse->getReturnValue();
    }
}

I have committed code in r9307 that seems to fix the issue; in call(), I wrap the call to the introspector in a try/catch block, and, if it fails, do not attempt to explicitly cast array/struct params. This will, of course, mean that ZF-2090 will still appear to be broken for any server that does not implement system.* methods. However, I think that's a reasonable tradeoff.

Please test (I tested with the incutio example in an above comment), and let me know if this is ready to commit to the release branch.

I also experienced this issue with XmlRpc requests to Wordpress that broke when upgrading to 1.5.1.

The changes in r9307 seem to solve the problem.

Just a side note, if using Matthew's suggestion to extend the class for yourself for now, make sure to use $this->getLastResponse() and not $this->_lastResponse.

Hi,

Will the fix for this make 1.5.3 or above?

It is interesting that Oren suffered the same fate as Wordpress uses the Incutio XML-RPC library!

Cheers,

  • Robert

I just tracked down an obscure bug I was having to this issue, and lo-and-behold there's already a ticket for it :)

So r9307 looks exactly like what the doctor ordered, and it fixes my problems.

Hey Matthew!

I am using Zend_XmlRpc_Server and Zend_XmlRpc_Client of the 1.5.2 release.

Now I am running into this issue as well - and don't ask me why, but the client "dies" when it attempts to guess the parameters required by the call. I figured this out because all of the sudden I have "system.methodSignature" in my request and after the request is issued, I just get "failed to parse" response.

And I was wondering why it never reached the actual class attached to the server. ;-)

I know you like debugging via a log, but there is absolutely nothing in my logs (parse errors or anything) and it would be nice if you added an optional debug that just outputs code on the screen or maybe a way to specify another logger to log what exactly is send out and received during a "single" call.

I also vote that I can turn off strict checks, I don't see a real benefit in this except for development. In a live environment every XMLRPC call equals two. I don't see why this should be done.

Added 'setSkipSystemLookup()' method to client, which disables system.methodSignature calls. Flag is false by default, enabling the check.

Changes are checked in to trunk and 1.5 and 1.6 release branches.