1
0
Fork 0

Replace Send/Recv funcs with In/Out channels.

Basically, make the channels public and just let the client handle a
stream/net error if it's interested.

This simplifies the API a little and also allows an application to
select {} from the XMPP channel and any other channels at the same time.
This commit is contained in:
Matt Goodall 2012-07-16 17:13:34 +01:00
parent e22b95e43e
commit cc012762e9
5 changed files with 50 additions and 61 deletions

View File

@ -19,15 +19,15 @@ func main() {
// Create stream and configure it as a client connection.
jid := must(xmpp.ParseJID(*jid)).(xmpp.JID)
stream := must(xmpp.NewStream(jid.Domain + ":5222", &xmpp.StreamConfig{LogStanzas: true})).(*xmpp.Stream)
x := must(xmpp.NewClientXMPP(stream, jid, *password, &xmpp.ClientConfig{InsecureSkipVerify: true})).(*xmpp.XMPP)
client := must(xmpp.NewClientXMPP(stream, jid, *password, &xmpp.ClientConfig{InsecureSkipVerify: true})).(*xmpp.XMPP)
log.Printf("Connection established for %s\n", x.JID)
log.Printf("Connection established for %s\n", client.JID)
// Announce presence.
x.Send(xmpp.Presence{})
client.Out <- xmpp.Presence{}
// Filter messages into dedicated channel and start a goroutine to log them.
_, messages := x.AddFilter(
_, messages := client.AddFilter(
xmpp.MatcherFunc(
func(v interface{}) bool {
_, ok := v.(*xmpp.Message)
@ -43,20 +43,16 @@ func main() {
// Log any stanzas that are not handled elsewhere.
go func() {
for {
stanza, err := x.Recv()
if err != nil {
log.Fatal(err)
}
log.Printf("* recv: %v\n", stanza)
for x := range client.In {
log.Printf("* recv: %v\n", x)
}
}()
// Get disco#info for home server.
info := &DiscoInfo{}
iq := xmpp.Iq{Id: xmpp.UUID4(), Type: "get", To: x.JID.Domain}
iq := xmpp.Iq{Id: xmpp.UUID4(), Type: "get", To: client.JID.Domain}
iq.PayloadEncode(info)
reply, _ := x.SendRecv(&iq)
reply, _ := client.SendRecv(&iq)
reply.PayloadDecode(info)
log.Printf("* info: %v\n", info)

View File

@ -19,14 +19,10 @@ func main() {
// Create stream and configure it as a component connection.
jid := must(xmpp.ParseJID(*jid)).(xmpp.JID)
stream := must(xmpp.NewStream(*addr, &xmpp.StreamConfig{LogStanzas: true})).(*xmpp.Stream)
x := must(xmpp.NewComponentXMPP(stream, jid, *secret)).(*xmpp.XMPP)
comp := must(xmpp.NewComponentXMPP(stream, jid, *secret)).(*xmpp.XMPP)
for {
v, err := x.Recv()
if err != nil {
log.Fatal(err)
}
log.Printf("recv: %v", v)
for x := range comp.In {
log.Printf("recv: %v", x)
}
}

View File

@ -21,22 +21,30 @@
stream, err := xmpp.NewStream("localhost:5347", nil)
X, err := xmpp.NewComponentXMPP(stream, jid, "secret")
Messages are sent using the XMPP.Send method, e.g. a client typically
announces its presence on the XMPP network as soon as it's connected:
Outgoing XMPP stanzas are sent to the XMPP instance's Out channel, e.g. a
client typically announces its presence on the XMPP network as soon as it's
connected:
X.Send(xmpp.Presence{})
X.Out <- xmpp.Presence{}
Incoming messages can be received in a simple loop, ended by an os.EOF for
clean shutdown or any other error for something unexpected. XMPP defines
four types of stanza: <error/>, <iq/>, <message/> and <presence/>
represented by Error, Iq, Message and Presence structs respectively.
Incoming messages are handled by consuming the XMPP instance's In channel.
The channel is sent all XMPP stanzas as well as terminating error (io.EOF
for clean shutdown or any other error for something unexpected). The
channel is also closed after an error.
XMPP defines four types of stanza: <error/>, <iq/>, <message/> and
<presence/> represented by Error, Iq, Message (shown below) and Presence
structs respectively.
for {
stanza, err := X.Recv()
if err == io.EOF {
break
for i := range X.In {
switch v := i.(type) {
case error:
log.Printf("error : %v\n", v)
case *xmpp.Message:
log.Printf("msg : %s says %s\n", v.From, v.Body)
default:
log.Printf("%T : %v\n", v, v)
}
log.Printf("%T : %v\n", stanza, stanza)
}
Note: A "bound" JID is negotatiated during XMPP setup and may be different

View File

@ -9,14 +9,21 @@ import (
// Handles XMPP conversations over a Stream. Use NewClientXMPP or
// NewComponentXMPP to create and configure a XMPP instance.
type XMPP struct {
// JID associated with the stream. Note: this may be negotiated with the
// server during setup and so must be used for all messages.
JID JID
stream *Stream
// Stanza channels.
in chan interface{}
out chan interface{}
// Channel of incoming messages. Values will be one of Iq, Message,
// Presence, Error or error. Will be closed at the end when the stream is
// closed or the stream's net connection dies.
In chan interface{}
// Channel of outgoing messages. Messages must be able to be marshaled by
// the standard xml package, however you should try to send one of Iq,
// Message or Presence.
Out chan interface{}
// Incoming stanza filters.
filterLock sync.Mutex
@ -28,34 +35,20 @@ func newXMPP(jid JID, stream *Stream) *XMPP {
x := &XMPP{
JID: jid,
stream: stream,
in: make(chan interface{}),
out: make(chan interface{}),
In: make(chan interface{}),
Out: make(chan interface{}),
}
go x.sender()
go x.receiver()
return x
}
// Send a stanza.
func (x *XMPP) Send(v interface{}) {
x.out <- v
}
// Return the next stanza.
func (x *XMPP) Recv() (interface{}, error) {
v := <-x.in
if err, ok := v.(error); ok {
return nil, err
}
return v, nil
}
func (x *XMPP) SendRecv(iq *Iq) (*Iq, error) {
fid, ch := x.AddFilter(IqResult(iq.Id))
defer x.RemoveFilter(fid)
x.Send(iq)
x.Out <- iq
stanza := <-ch
reply, ok := stanza.(*Iq)
@ -156,19 +149,19 @@ type filter struct {
}
func (x *XMPP) sender() {
for v := range x.out {
for v := range x.Out {
x.stream.Send(v)
}
}
func (x *XMPP) receiver() {
defer close(x.in)
defer close(x.In)
for {
start, err := x.stream.Next()
if err != nil {
x.in <- err
x.In <- err
return
}
@ -200,7 +193,7 @@ func (x *XMPP) receiver() {
}
if !filtered {
x.in <- v
x.In <- v
}
}
}

View File

@ -51,14 +51,10 @@ func main() {
}
// Signal presence.
x.Send(xmpp.Presence{})
x.Out <- xmpp.Presence{}
// Log anything that arrives.
for {
stanza, err := x.Recv()
if err != nil {
log.Fatal(err)
}
for stanza := range x.In {
log.Printf("recv: %T %v", stanza, stanza)
}
}