go-xmpp 0.3.0

Jérôme Sautret
· 2 min read
Send by email

A new version of the go-xmpp library, which can be used to write XMPP clients or components in Go, as been released. It’s available on GitHub.

Upon new features, it adds a websocket transport. For this reason, the minimum go version to use it is now 1.13. It also adds a SendIQ method, to send iq stanza and receive the response asynchronously on a channel.
On the component side, it fixes a SIGSEGV in xmpp_component (#126) and adds more tests for the Component code.

A small example

Writing an XMPP component

Speaking of components, here is a simple example on how to create a simple one. As a reminder, components are external services that can communicate with an XMPP service using the Jabber Component Protocol as described in XEP-0114.

A component has its own XMPP domain and must know the server address and service port:

     const (
        domain  = "mycomponent.localhost"
        address = "localhost:8888"
    )

The options needed when creating a new component are defined as follow (secret must match the one define in the server config):

    opts := xmpp.ComponentOptions{
        TransportConfiguration: xmpp.TransportConfiguration{
            Address: address,
            Domain:  domain,
        },
        Domain:   domain,
        Secret:   "secret",
    }

To actually create the simplest component, just create a default route, and pass it to NewComponent, as well as the above options:

    router := xmpp.NewRouter()
    c, err := xmpp.NewComponent(opts, router)

Connect establishes the XMPP connection to the server, and authenticates with it:

      err := c.Connect()

Now we can try to send a disco iq to the server:

       iqReq := stanza.NewIQ(stanza.Attrs{Type: stanza.IQTypeGet,
        From: domain,
        To:   "localhost",
        Id:   "my-iq1"})
    disco := iqReq.DiscoInfo()
    iqReq.Payload = disco

In order to get the response asynchronously, the sendIq will return a channel where we expect to receive the result iq. We also need to pass it a context to set a timeout:

    ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
    res, _ := c.SendIQ(ctx, iqReq)

Now we just have to wait for our response:

    select {
    case iqResponse := <-res:
        // Got response from server
        fmt.Print(iqResponse.Payload)
    case <-time.After(100 * time.Millisecond):
        cancel()
        panic("No iq response was received in time")
    }

Full example

The full program that runs a component, connects it to a XMMP server and perform a disco on it to display the result is thus as is:

package main

import (
    "context"
    "fmt"
    "time"

    xmpp "github.com/FluuxIO/go-xmpp"
    "gosrc.io/xmpp/stanza"
)

const (
    domain  = "mycomponent.localhost"
    address = "build.vpn.p1:8888"
)

// Init and return a component
func makeComponent() *xmpp.Component {
    const (
        domain  = "mycomponent.localhost"
        address = "build.vpn.p1:8888"
    )
    opts := xmpp.ComponentOptions{
        TransportConfiguration: xmpp.TransportConfiguration{
            Address: address,
            Domain:  domain,
        },
        Domain: domain,
        Secret: "secret",
    }
    router := xmpp.NewRouter()
    c, err := xmpp.NewComponent(opts, router)
    if err != nil {
        panic(err)
    }
    return c
}

func main() {
    c := makeComponent()

    // Connect Component to the server
    fmt.Printf("Connecting to %v\n", address)
    err := c.Connect()
    if err != nil {
        panic(err)
    }

    // make a disco iq
    iqReq := stanza.NewIQ(stanza.Attrs{Type: stanza.IQTypeGet,
        From: domain,
        To:   "localhost",
        Id:   "my-iq1"})
    disco := iqReq.DiscoInfo()
    iqReq.Payload = disco

    // res is the channel used to receive the result iq
    ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
    res, _ := c.SendIQ(ctx, iqReq)

    select {
    case iqResponse := <-res:
        // Got response from server
        fmt.Print(iqResponse.Payload)
    case <-time.After(100 * time.Millisecond):
        cancel()
        panic("No iq response was received in time")
    }
}