QxRPC
From qtnode
See QxtXmlRpc for the current version of the library, this page is not up to date any more.
Contents |
What is it?
So, what is QxRPC supposed to be? For my purpose, I simply need a library that
- provides XML-RPC functionality (and of course, independently of the actual implementation of the server)
- is portable (runs on Linux, Windows, Mac OS without any further efforts)
- is nicely integrated with Qt (pass in QObjects, get out QObjects)
- doesn't block the user interface when operating (i.e. asynchronous; emits signal when finished)
- optionally supports SSL encryption
ahigerd had some further going plans (that I didn't quite understand, unfortunately), some stuff about signals and slots, he will have to explain that to me once more, I'm afraid.
What does it provide?
I thought that we should start with some kind of UML design, at least of the part that the user of our lib will see later on. Let's say there is some class
class QXmlRpcClient : public QObject {
Q_OBJECT
public:
QXmlRpcClient(QString serverUrl);
public slots:
int call(QString procName, QVariant params);
signals:
void finished(int callId, QVariant retValues);
}
or the like (not looking at what a backend would do), then call would return an ID that later can be used to map the result you got to the call you did. This is useful when you do multiple calls at the same time that may return in different order. This also makes it possible to easily subclass this with a class that fits the specific needs, such as
class MyXmlRpcClient : public QXmlRpcClient {
Q_OBJECT
public:
MyXmlRpcClient(QString serverUrl);
public slots:
login(QString myname, QString mypw); // this->call('login', ...);
getMessages(QString session_id); // this->call('get_messages', ...);
logout(); // this->call('logout', ...);
signals:
void loggedin(bool success); // emitted on finished login
void gotMessages(QList<QString> msgs); // emitted on finished getMessages
void loggedout(bool success); // emitted on finished logout
}
Well, I don't know how naive my approach is, since I did almost no work in designing libraries (in C++) before. And maybe this isn't at all what ahigerd imagined, this is simply what I thought could be useful.
How does it work?
Behind the scenes, we can use what QHttp, QTcpSocket and the QtXml module provide. We will have to read what the XML-RPC standard actually wants us to behave. I have not much experience in doing low-level network coding, but actually the Qt classes should do the real low-level work.
For SSL connections, there is a commercial solution from Trolltech; if we want that, we probably will have to rewrite it by hand or use OpenSSL by other means.
--Tgp 17:43, 12 October 2006 (CDT)
ahigerd's grand scheme
Ultimately, I'd like XML-RPC to be a special case of a more generic Qt signal-based RPC mechanism where you can put a signal in one end and get the same signal out on the other end, which you can connect to a slot as normal. I've got some various ideas on how this may be implemented, some more arcane (but more convenient) than others. There may be some deep magic involved with moc and meta-objects.
The simplest interface will actually be the same no matter how I handle the Qt magic; the backend will do the appropriate magic and make the same call. It should be something like this in practice:
rpc.remoteCall("functionName", param1, param2, param3);
The data types of param1, param2, and param3 will use metatype magic to determine what XML-RPC data type to associate with each parameter. (Since XML-RPC supports nested values, I may need to make some sort of helper class. This is a detail I'll handle later; it should be a simple matter to extend a flat parameter list to support it once it's working.)
One possible signal-relaying architecture I'm considering looks something like this:
rpc.relaySignal(myObject, SIGNAL(mySignal(int)));
This would replace the normal QObject::connect() call for the signal and, when myObject emits the specified signal, would wrap around
rpc.remoteCall("mySignal", param1);
Ideally there will be symmetry involved, so it's more of a peer-to-peer protocol than a client/server protocol. Receiving the response from the RPC peer would look something like this:
rpc.attachSignal("foreignSignal", myObject, SLOT(mySlot(int)));
And then mySlot will be called with the parameters from the RPC response. The XML-RPC protocol is stateful, however, so for communicating with XML-RPC hosts instead of QxRPC peers, it would look like this:
rpc.attachSignal("xmlRpcResponse", myObject, SLOT(mySlot(int)));
I'm still pondering a little bit over how to handle the ambiguity that may arise from concurrent requests; I'm sure I'll figure something out. I'm also considering how to handle connection behavior; perhaps the QxRPC object can be created in client, server, or peer modes.
Possible sources of magic
A signal's signature contains all of the necessary information to make this work, so there's a possibility it may end up looking something like
rpc.remoteCall(SIGNAL(functionName(type1,type2,type3)), param1, param2, param3);
This isn't a wholly unacceptable API, in my opinion. QVariant also has the ability to report what data type is stored within it, so that's another possibility, although typecasting may cause it to report a different type. For pure XML-RPC this isn't a problem, but for my vision of the full Qt-aware QxRPC this may be a problem.
current state
I have written a class that parses XML-RPC methodResponses and then provides the return values as QVariants. Works rather well, just needs more comments, more stability on non-XML-RPC data (error handling) and the like.
tgp 17:34, 16 October 2006 (CDT)
I've successfully written a proof-of-concept implementation of a signal deserializer and I've decided on an API. Serialization shouldn't be too terribly difficult but it may require yet more magic. As it stands, there's already macro magic necessary to support passing any data type without an explicit QVariant constructor.
I already have determined that I'm going to have to disable moc for the class in order to override qt_metacall to capture the identity of an incoming signal. It's not going to be pretty but it'll work. It'll probably break in subclassing, so I'm intentionally not declaring anything virtual.
ahigerd 23:53, 23 October 2006 (CDT)
For my part of the library, you can now compose XML-RPC calls passing in QVariants, do the actual method call using QHttp and parse the response with the class described above. It all needs more stability and security, but for non-evil XML-RPC servers, it should be ok.
tgp 08:24, 24 October 2006 (CDT)
Signal serialization and deserialization via QVariant is complete and compiles. I wouldn't trust its stability yet, though; I took some rather unsafe shortcuts for this phase. There's a stub in place for transmitting signals and a function that a response reader should invoke.
Don't use it yet, but the concept is there.
ahigerd 21:39, 28 October 2006 (CDT)
Safety improved now. Don't pass a string representation of something that needs to be passed as a numeric data type; it won't work.
ahigerd 14:18, 29 October 2006 (CST)
Decided on the network framework. Not sure how well tgp's classes are going to work in my class structure; probably will copy the protocol stuff and reimp the network stuff myself. Some of the basic networking stuff is implemented, but right now there's pretty much no communication over the socket.
ahigerd 21:52, 7 November 2006 (CST)