If you take a look at https://tools.ietf.org/html/rfc6120#section-8.3.2 then an error packet can optionally include the original request - [OPTIONAL to include sender XML here]
However, if they do so, I am finding it very hard to parse such a response if it’s an IQ error. In fact, I suspect it’s currently impossible (!). Below is a sample test case I’ve written to demonstrate the problem. Essentially the pull parser examines the first tag, finds a command with a specific namespace, and delegates the parsing to the provider for that namespace. That provider has no knowledge that it’s an error packet, and cannot do so, as it can only parse the information in the tags.
Having pondered this a while, one solution would be to add a new type of Provider, that has an abstract method parse(XmlPullParser parser, int initialDepth, IQ.Type type) or similar - the custom provider could then use the additional IQ.Type to determine if it’s an error packet or not. I’m happy to submit a PR on that basis, but figured others may have a better solution (@Flow?).
Any thoughts welcomed (test code is below),
Greg
private String requestXML = "<iq id='sid' type='get' from='from@example.com' to='to@example.com'>\n" +
" <command xmlns='http://jabber.org/protocol/commands' node='http://example.com' action='execute'>\n" +
" </command>\n" +
"</iq>\n";
private String errorXML = "<iq id='sid' type='error' from='from@example.com' to='to@example.com'>\n" +
" <error type='cancel'>\n" +
" <bad-request xmlns='urn:ietf:params:xml:ns:xmpp-stanzas'/>\n" +
" </error>\n" +
"</iq>\n";
private String errorWithRequestXML = "<iq id='sid' type='error' from='from@example.com' to='to@example.com'>\n" +
" <command xmlns='http://jabber.org/protocol/commands' node='http://example.com' action='execute'>\n" +
" </command>\n" +
" <error type='cancel'>\n" +
" <bad-request xmlns='urn:ietf:params:xml:ns:xmpp-stanzas'/>\n" +
" </error>\n" +
"</iq>\n";
@Test
public void parseAnErrorPacket() throws Exception {
ProviderManager.addIQProvider("command", "http://jabber.org/protocol/commands", new TestIQProvider());
// Verify we can parse a request
final Stanza requestStanza = PacketParserUtils.parseStanza(requestXML);
// This works - the stanza is recognised as a TestIQ
assertThat(requestStanza, is(instanceOf(TestIQ.class)));
// Verify we can parse an error
final Stanza errorStanza = PacketParserUtils.parseStanza(errorXML);
// This works - the stanza is recognised as an ErrorIQ
assertThat(errorStanza, is(instanceOf(ErrorIQ.class)));
// Verify we can parse the other form of an error
final Stanza errorWithRequestStanza = PacketParserUtils.parseStanza(errorWithRequestXML);
// This fails - the stanza is recognised as a TestIQ, despite being of type Error
assertThat(errorWithRequestStanza, is(instanceOf(ErrorIQ.class)));
}
private class TestIQProvider extends IQProvider<TestIQ> {
@Override
public TestIQ parse(final XmlPullParser parser, final int initialDepth) {
return new TestIQ();
}
}
private class TestIQ extends IQ {
public TestIQ() {
super("command", "http://jabber.org/protocol/commands");
}
@Override
protected IQChildElementXmlStringBuilder getIQChildElementBuilder(final IQChildElementXmlStringBuilder xml) {
return null;
}
}