go-xmpp 0.3.0
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")
}
}