powered by Jive Software

Parsing error packets


#1

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;
        }
    }

#2

Overnight, I cam up with an alternative, potentially simpler, solution; simply force the code flow in PacketParserUtils.parseIQ() down the PacketParserUtils.parseError() flow if the IQ.Type is Error. However, comments on any preferred solution are welcome!

Greg


#3

Error stanzas are not specialized types in Smack. They are normal Stanza types, either subclasses of IQ, Presence or Message, with the type field set to Stanza.Type.error.

I’m not sure why that is. Your ad-hoc command IQ error response parses fine for me:

@Test
public void parseErrorWithRequest() throws Exception {
    final String errorWithRequest = "<iq id='sid' type='error' from='from@example.com' to@example.com'>"
                    + "<command xmlns='http://jabber.org/protocol/commands' ='http://example.com' action='execute'>"
                    + "</command>"
                    + "<error type='cancel'>"
                    +    "<bad-request xmlns='urn:ietf:params:xml:ns:xmpp-stanzas'/>"
                    + "</error>"
                    + "</iq>";

    final Stanza requestStanza = PacketParserUtils.parseStanza(errorWithRequest);
    final AdHocCommandData adHocIq = (AdHocCommandData) requestStanza;

    assertEquals(IQ.Type.error, adHocIq.getType());
    assertEquals(AdHocCommand.Action.execute, adHocIq.getAction());

    StanzaError error = adHocIq.getError();
    assertEquals(StanzaError.Type.CANCEL, error.getType());
    assertEquals(StanzaError.Condition.bad_request, error.getCondition());
}

#4

Doh! So it does. I was focusing on the class of the parsed stanza, not the type. Apologies for noise!

Greg