xmpp/design/18_iqmux.md

5.0 KiB

Proposal: Implement a method of muxing based on IQ payloads

Author(s): Sam Whited
Last updated: 2020-02-17
Discussion: https://mellium.im/issue/18

Abstract

An API is needed for multiplexing based on IQ payloads.

Background

The current multiplexer only matches on top level elements. While the mux could be nested to provide support for matching on element children, this would mean that handlers written for children could accidentally be registered at the top level muxer. Some other mechanism is needed to make it obvious that handlers written to match against IQ child elements aren't meant to be registered against the top level muxer. To this end a new IQMux will be introduced that can be registered with the top level multiplexer to handle all IQ elements and can take its own custom handlers meant for matching IQ payloads.

Requirements

  • Ability to mux IQ stanzas based on XML name (and possibly other factors)
  • IQ handlers must be distinguishable from general XMPP handlers

Proposal

The proposed API creates three new types and seven new functions that would need to remain backwards compatible after we reach 1.0.

type IQHandler interface {
	HandleIQ(iq stanza.IQ, t xmlstream.TokenReadEncoder, start *xml.StartElement) error
}
    IQHandler responds to an IQ stanza.

type IQHandlerFunc func(iq stanza.IQ, t xmlstream.TokenReadEncoder, start *xml.StartElement) error
    The IQHandlerFunc type is an adapter to allow the use of ordinary functions
    as IQ handlers. If f is a function with the appropriate signature,
    IQHandlerFunc(f) is an IQHandler that calls f.

func (f IQHandlerFunc) HandleIQ(iq stanza.IQ, t xmlstream.TokenReadEncoder, start *xml.StartElement) error
    HandleIQ calls f(iq, t, start).

type IQMux struct {
	// Has unexported fields.
}
    IQMux is an XMPP multiplexer meant for handling IQ payloads.

    IQs are matched by the type and the XML name of their first child element
    (if any). If either the namespace or the localname is left off, any
    namespace or localname will be matched. Full XML names take precedence,
    followed by wildcard localnames, followed by wildcard namespaces.

    Unlike get and set type IQs, result IQs may have no child element, and error
    IQs may have more than one child element. Because of this it is normally
    adviseable to register handlers for type Error without any filter on the
    child element since we cannot guarantee what child token will come first and
    be matched against. Similarly, for IQs of type result, it is important to
    note that the start element passed to the handler may be nil, meaning that
    there is no child element.

func NewIQMux(opt ...IQOption) *IQMux
    NewIQMux allocates and returns a new IQMux.

func (m *IQMux) HandleXMPP(t xmlstream.TokenReadEncoder, start *xml.StartElement) error
    HandleXMPP dispatches the IQ to the handler whose pattern most closely
    matches start.Name.

func (m *IQMux) Handler(iqType stanza.IQType, name xml.Name) (h IQHandler, ok bool)
    Handler returns the handler to use for an IQ payload with the given name and
    type. If no handler exists, a default handler is returned (h is always
    non-nil).

type IQOption func(m *IQMux)
    IQOption configures an IQMux.

func ErrorIQ(h IQHandler) IQOption
    ErrorIQ is a shortcut for HandleIQ with the type set to "error" and a
    wildcard XML name.

    This differs from the other IQ types because error IQs may contain one or
    more child elements and we cannot guarantee the order of child elements and
    therefore won't know which element to match on. Instead it is normally wise
    to register a handler for all error type IQs and then skip or handle
    unnecessary payloads until we find the error itself.

func GetIQ(n xml.Name, h IQHandler) IQOption
    GetIQ is a shortcut for HandleIQ with the type set to "get".

func HandleIQ(iqType stanza.IQType, n xml.Name, h IQHandler) IQOption
    HandleIQ returns an option that matches the IQ payload by XML name and IQ
    type. For readability, users may want to use the GetIQ, SetIQ, ErrorIQ, and
    ResultIQ shortcuts instead.

    For more details, see the documentation on IQMux.

func HandleIQFunc(iqType stanza.IQType, n xml.Name, h IQHandlerFunc) IQOption
    HandleIQFunc returns an option that matches the IQ payload by XML name and
    IQ type.

func ResultIQ(n xml.Name, h IQHandler) IQOption
    ResultIQ is a shortcut for HandleIQ with the type set to "result".

    Unlike IQs of type get, set, and error, result type IQs may or may not
    contain a payload. Because of this it is important to check whether the
    start element is nil in handlers meant to handle result type IQs.

func SetIQ(n xml.Name, h IQHandler) IQOption
    SetIQ is a shortcut for HandleIQ with the type set to "set".