Send/Receive IQ Stanzas¶
Unlike Message
and
Presence
stanzas which only use
text data for basic usage, Iq
stanzas
require using XML payloads, and generally entail creating a new
Slixmpp plugin to provide the necessary convenience methods to
make working with them easier.
Basic Use¶
XMPP’s use of Iq
stanzas is built around
namespaced <query />
elements. For clients, just sending the
empty <query />
element will suffice for retrieving information. For
example, a very basic implementation of service discovery would just
need to be able to send:
<iq to="user@example.com" type="get" id="1">
<query xmlns="http://jabber.org/protocol/disco#info" />
</iq>
Creating Iq Stanzas¶
Slixmpp provides built-in support for creating basic Iq
stanzas this way. The relevant methods are:
These methods all follow the same pattern: create or modify an existing
Iq
stanza, set the 'type'
value based
on the method name, and finally add a <query />
element with the given
namespace. For example, to produce the query above, you would use:
self.make_iq_get(queryxmlns='http://jabber.org/protocol/disco#info',
ito='user@example.com')
Sending Iq Stanzas¶
Once an Iq
stanza is created, sending it
over the wire is done using its send()
method, like any other stanza object. However, there are a few extra
options to control how to wait for the query’s response.
These options are:
block
: The default behaviour is thatsend()
will block until a response is received and the response stanza will be the return value. Settingblock
toFalse
will cause the call to return immediately. In which case, you will need to arrange some way to capture the response stanza if you need it.timeout
: When using the blocking behaviour, the call will eventually timeout with an error. The default timeout is 30 seconds, but this may be overidden two ways. To change the timeout globally, set:self.response_timeout = 10
To change the timeout for a single call, the
timeout
parameter works:iq.send(timeout=60)
callback
: When not using a blocking call, using thecallback
argument is a simple way to register a handler that will execute whenever a response is finally received. Using this method, there is no timeout limit. In case you need to remove the callback, the name of the newly created callback is returned.cb_name = iq.send(callback=self.a_callback) # ... later if we need to cancel self.remove_handler(cb_name)
Properly working with Iq
stanzas requires
handling the intended, normal flow, error responses, and timed out
requests. To make this easier, two exceptions may be thrown by
send()
: IqError
and IqTimeout
. These exceptions only
apply to the default, blocking calls.
try:
resp = iq.send()
# ... do stuff with expected Iq result
except IqError as e:
err_resp = e.iq
# ... handle error case
except IqTimeout:
# ... no response received in time
pass
If you do not care to distinguish between errors and timeouts, then you
can combine both cases with a generic XMPPError
exception:
try:
resp = iq.send()
except XMPPError:
# ... Don't care about the response
pass
Advanced Use¶
Going beyond the basics provided by Slixmpp requires building at least a
rudimentary Slixmpp plugin to create a stanza object for
interfacting with the Iq
payload.
See also
The typical way to respond to Iq
requests is
to register stream handlers. As an example, suppose we create a stanza class
named CustomXEP
which uses the XML element <query xmlns="custom-xep" />
,
and has a plugin_attrib
value
of custom_xep
.
There are two types of incoming Iq
requests:
get
and set
. You can register a handler that will accept both and then
filter by type as needed, as so:
self.register_handler(Callback(
'CustomXEP Handler',
StanzaPath('iq/custom_xep'),
self._handle_custom_iq))
# ...
def _handle_custom_iq(self, iq):
if iq['type'] == 'get':
# ...
pass
elif iq['type'] == 'set':
# ...
pass
else:
# ... This will capture error responses too
pass
If you want to filter out query types beforehand, you can adjust the matching
filter by using @type=get
or @type=set
if you are using the recommended
StanzaPath
matcher.
self.register_handler(Callback(
'CustomXEP Handler',
StanzaPath('iq@type=get/custom_xep'),
self._handle_custom_iq_get))
# ...
def _handle_custom_iq_get(self, iq):
assert(iq['type'] == 'get')