IQ Stanzas
IQ (Info/Query) stanzas are XMPP's method of requesting and modifying information, similar to HTTP's GET and POST methods. They form the foundation for most XMPP protocol interactions beyond simple messaging and presence.
Understanding IQ Stanzas
Each IQ stanza must have an id attribute that associates the request with its response. XMPP entities must always respond to IQ stanzas of type get or set with a corresponding result or error response.
IQ Types
IQ stanzas have four possible types:
get: Request information from the server or another entityset: Request modification of information or stateresult: Response to a successfulgetorsetrequesterror: Response indicating an error occurred
Basic IQ Usage
Creating and sending an IQ stanza in Whixp:
final iq = IQ(generateID: true)
..type = 'get'
..to = JabberID('server@example.com')
..payload = DiscoInformation();
final result = await iq.send(whixp.transport);
Sending IQ Stanzas
The send method on an IQ stanza returns a Future<IQ> that completes when the response is received:
final iq = IQ(generateID: true)
..type = 'get'
..to = JabberID('user@example.com')
..payload = Version();
try {
final result = await iq.send(
whixp.transport,
timeout: 10, // seconds
);
if (result.type == 'result') {
// Process successful response
print('IQ request succeeded');
} else if (result.type == 'error') {
// Handle error
print('IQ request failed: ${result.error?.text}');
}
} catch (e) {
print('IQ request timed out or failed: $e');
}
Callbacks
You can provide callbacks for handling responses:
final iq = IQ(generateID: true)
..type = 'get'
..payload = DiscoInformation();
await iq.send(
whixp.transport,
callback: (result) {
// Called when result is received
print('Received result: ${result.payload}');
},
failureCallback: (error) {
// Called when error is received
print('Error: ${error.reason} - ${error.text}');
},
timeoutCallback: () {
// Called when request times out
print('Request timed out');
},
timeout: 5,
);
IQ Payloads
IQ stanzas can contain various payloads depending on the operation:
Service Discovery
// Discover server information
final info = DiscoInformation();
final iq = IQ(generateID: true)
..type = 'get'
..payload = info;
final result = await iq.send(whixp.transport);
if (result.payload is DiscoInformation) {
final disco = result.payload as DiscoInformation;
for (final feature in disco.features) {
print('Feature: ${feature.variable}');
}
}
Version Information
final version = Version();
final iq = IQ(generateID: true)
..type = 'get'
..payload = version;
final result = await iq.send(whixp.transport);
Custom Payloads
You can create custom payloads by extending the Stanza class:
class CustomPayload extends Stanza {
final String data;
CustomPayload(this.data);
XmlElement toXML() => WhixpUtils.xmlElement(
'custom',
attributes: {'data': data},
);
String get name => 'custom';
}
final iq = IQ(generateID: true)
..type = 'set'
..payload = CustomPayload('example');
await iq.send(whixp.transport);
Error Handling
IQ stanzas can return error responses. Always check for errors:
final iq = IQ(generateID: true)
..type = 'get'
..payload = DiscoInformation();
final result = await iq.send(whixp.transport);
if (result.error != null) {
final error = result.error!;
print('Error type: ${error.type}');
print('Error condition: ${error.condition}');
print('Error text: ${error.text}');
}
Timeout Handling
IQ requests can timeout if no response is received. The default timeout is 5 seconds, but you can customize it:
final iq = IQ(generateID: true)
..type = 'get'
..payload = DiscoInformation();
try {
final result = await iq.send(
whixp.transport,
timeout: 30, // 30 seconds
);
} on StanzaException catch (e) {
if (e is TimeoutException) {
print('Request timed out after 30 seconds');
}
}
Listening to IQ Responses
While the send method handles responses automatically, you can also listen to incoming IQ stanzas:
whixp.addEventHandler<IQ>('iq', (iq) {
if (iq == null) return;
// Handle incoming IQ stanzas
if (iq.type == 'get' && iq.payload is DiscoInformation) {
// Respond to service discovery request
final response = IQ(generateID: true)
..type = 'result'
..to = iq.from
..id = iq.id;
whixp.send(response);
}
});
Best Practices
- Always generate IDs: Use
generateID: truewhen creating IQ stanzas to ensure unique identification - Handle timeouts: Always provide timeout handling for IQ requests
- Check for errors: Always verify that responses don't contain errors before processing
- Use appropriate types: Use
getfor queries andsetfor modifications - Match request IDs: When responding to IQ requests, ensure the response ID matches the request ID
Common IQ Operations
Service Discovery
// Discover server features
final info = DiscoInformation();
final iq = IQ(generateID: true)
..type = 'get'
..payload = info;
final result = await iq.send(whixp.transport);
Version Query
// Query server version
final version = Version();
final iq = IQ(generateID: true)
..type = 'get'
..payload = version;
final result = await iq.send(whixp.transport);
IQ stanzas are the foundation for most advanced XMPP features. Understanding how to use them effectively is essential for building robust XMPP applications.