Skip to content
Back to top
View this page

# 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:

- `make_iq()`

- `make_iq_get()`

- `make_iq_set()`

- `make_iq_result()`

- `make_iq_error()`

- `make_iq_query()`

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, as well as how to handle the result.

`send()` returns an `Future` object, which can be awaited on until a `result` is received.

These options are:

- `timeout`: When using the blocking behaviour, the call will eventually timeout with an error. The default timeout is 30 seconds, but this may be overridden 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 the `callback` argument is a simple way to register a handler that will execute whenever a response is finally received.

  >     iq.send(callback=self.a_callback)

Note

`callback` can be effectively replaced using `await`, and standard exception handling (see below), which provide a more linear and readable workflow.

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 = await 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 = await 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

- Creating a Slixmpp Plugin

- How to Work with Stanza Objects

- Using Stream Handlers and Matchers

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')