XMPP client basics

headstock is a Python library offering a simple interface to write XMPP client applications. This tutorial will guide you through the various aspects of the library.

First make sure you have followed the Requirements guide prior to go through this tutorial.

Goals

The main idea behind headstock is not to provide a complete API mapping the XMPP specifications but to offer a very basic set of tools to:

  • handle the connection
  • create the XMPP stream
  • deal with incoming stanzas with as little overhead on your application as possible

It tries to act as library rather than a framework as much as possible.

Initialize the XMPP client and stream

The most basic use of the client:

>>> from headstock.client import AsyncClient
>>> c = AsyncClient(u'user@domain', u'secret', hostname='localhost', port=5222)
>>> c.run()

This will not do much aside from setting the XMPP stream up and connect initiate a XMPP session with the server.

Note that the run method will block. To stop a client you may call stop.

Note

The default client uses the asyncore module to perform the socket handling but you may also use a Kamaelia or Tornado based client instead.

Enable TLS support

TLS can be enabled by setting the tls parameter to True. TLS is provided by the ssl module.

Enable logging

You may log the XPP stream to a file and/or to the standard output.

>>> c.set_log(path=path_to_file, stdout=True)

Warning

The logging may be quite expensive on to your client as each incoming and outgoing stanza will have to be serialized to a XML string first. This is not an issue for most cases aside from where you want to put performance first.

Handling stanza

headstock main goal is not to be too intrusive on your development of a XMPP client which is why it doesn’t require you inherit from a given class nor does it put too many constraints on how you want to design your client.

Processing incoming stanzas is done by what headstock refers to XMPP handlers. Those are merely callables with a few properties which can be set by the headstock.xmpphandler() decorator.

Sending stanzas is simple as a calling the appropriate methods on the client instance, meaning your class is only required to hold a reference to that instance to be able to send stanza to the server.

Receive stanzas

In order to make the client actually do something, you must register an instance of a class which defines some XMPP handler as follow:

import headstock
from bridge.common import XMPP_CLIENT_NS, XMPP_ROSTER_NS

class Basic(object):
    @headstock.xmpphandler('item', XMPP_ROSTER_NS)
    def roster(self, e):
        print "Contact '%s' %s with subscription: %s" % (e.get_attribute_value('name', ''),
                                                         e.get_attribute_value('jid', ''),
                                                         e.get_attribute_value('subscription', ''))

    @headstock.xmpphandler('presence', XMPP_CLIENT_NS)
    def presence(self, e):
        print "Received '%s' presence from: %s" % (e.get_attribute_value('type', 'available'),
                                                   e.get_attribute_value('from'))

basic = Basic()
c.register(basic)

The headstock.xmpphandler() decorator tells the client which stanza it expects to receive. It uses qualified name of stanzas to do so.

It also accepts two other parameters allowing to unregister the handler once it has been called the first time. The other one allows to forget the matched stanza once the handler was applied. This ensures memory won’t grow out of hand.

Note that your handler may return a bridge.Element which will be serialized and sent onto the wire.

To remove an instance from being used, you can call:

c.unregister(basic)

Register on IQ stanzas based on their type and/or id

In some circumstances you may need to react to a stanza like fhe following:

<iq id="aab" type="result" />

One cannot register a handler using the headstock.xmpphandler() decorator to such stanza. Instead you can do this:

self.client.register_on_iq(somefunc, type="result", id="aab", once=True)

This will call somefunc(e) when the appropriate stanza is received. Setting the parameter once ensures it will be unregistered automatically as well.

Send stanzas

The headstock.xmpphandler() decorator is a one-way track. It tells the client where to dispatch incoming stanzas and permits to respond to received stanza but not more.

To send stanza you need a reference to the client instance and call:

  • send_stanza(e)
  • send_raw_stanza(string)

The first one expects a bridge.Element instance whilst the other one expects just a string to be sentd as-is on the wire. This means you do not have to use bridge to generate your stanzas.

from headstock.lib.utils import generate_unique
from bridge import Element as E
from bridge.common import XMPP_CLIENT_NS

class Basic(object):
    def ready(self, client):
        self.client = client

    def message(self, jid, text):
        m = E(u"message", attributes={u'from': unicode(self.client.jid), u'to': unicode(jid), u'type': u'chat', u'id': generate_unique()}, namespace=XMPP_CLIENT_NS)
        E(u'body', content=text, namespace=XMPP_CLIENT_NS, parent=m)

        self.client.send_stanza(m)

b = Basic()
b.message("somefriend@domain", u"blah blah")

The trick to make your class able to use the client instance is to declare a ready() method which will be called by the client once the session has been established.

Cleanup resources

Your classes may need to perform some operations when the client shuts down. To do so your class must declare some methods:

class Basic(object):
    def stopping(self):
        # Called before the socket is closed
        # unless it was closed by the server
        # already
        pass

    def cleanup(self):
        # Called after the connection was closed
        pass

    def terminated(self):
        # Called at the very end of the
        # shutdown process
        pass

Register new users

In order to register your user you just need to set the registerclass parameter of the client class to a class which subclass the headstock.Register class.

>>> from headstock.client import AsyncClient
>>> from headstock.register import Register
>>> c = AsyncClient(u'user@domain', u'secret', hostname='localhost', port=5222, registerclass=Register)
>>> c.run()

The default class will perform the registration exchange but if you want control over the various steps or outcomes (success, conflict, constraint, etc.) you will have to subclass the headstock.register.Register class and implement:

  • handle_register_success(e)
  • handle_register_conflict(e)
  • handle_resource_constraint(e)