# HG changeset patch # User Chris Jones # Date 1325181741 25200 # Node ID a456133ed0ac7bdc1b5b0c0206b94a5f0072df5f # Parent 78961db80baef8d332650dbb35c820bc8d8f553e Don't accept data on Client.Out until resource binding is complete. StartSession() won't do its work until after this happens. That means the app can call StartSession() and wait for it to return before checking Client.Jid. diff -r 78961db80bae -r a456133ed0ac examples/interact.go --- a/examples/interact.go Thu Dec 29 09:48:36 2011 -0700 +++ b/examples/interact.go Thu Dec 29 11:02:21 2011 -0700 @@ -30,6 +30,11 @@ } defer c.Close() + err = c.StartSession(&xmpp.Presence{}) + if err != nil { + log.Fatalf("StartSession: %v", err) + } + go func(ch <-chan xmpp.Stanza) { for obj := range ch { fmt.Printf("s: %v\n", obj) diff -r 78961db80bae -r a456133ed0ac stream.go --- a/stream.go Thu Dec 29 09:48:36 2011 -0700 +++ b/stream.go Thu Dec 29 11:02:21 2011 -0700 @@ -195,6 +195,9 @@ default: send = true } + if !send { + continue + } st, ok := x.(Stanza) if !ok { log.Printf("Unhandled non-stanza: %v", @@ -213,14 +216,29 @@ } } -// BUG(cjyar) Disable this loop until resource binding is -// complete. Otherwise the app might inject something weird into our -// negotiation stream. -func writeStream(srvOut chan<- interface{}, cliIn <-chan Stanza) { +// This loop is paused until resource binding is complete. Otherwise +// the app might inject something inappropriate into our negotiations +// with the server. The control channel controls this loop's +// activity. +func writeStream(srvOut chan<- interface{}, cliIn <-chan Stanza, + control <-chan int) { defer tryClose(srvOut, cliIn) - for x := range cliIn { - srvOut <- x + var input <-chan Stanza + for { + select { + case status := <- control: + switch status { + case 0: + input = nil + case 1: + input = cliIn + case -1: + break + } + case x := <- input: + srvOut <- x + } } } @@ -513,7 +531,8 @@ } cl.Jid = *jid log.Printf("Bound resource: %s", cl.Jid.String()) - return true + cl.bindDone() + return false } cl.HandleStanza(msg.Id, f) cl.xmlOut <- msg diff -r 78961db80bae -r a456133ed0ac xmpp.go --- a/xmpp.go Thu Dec 29 09:48:36 2011 -0700 +++ b/xmpp.go Thu Dec 29 11:02:21 2011 -0700 @@ -45,10 +45,8 @@ // The client in a client-server XMPP connection. type Client struct { - // This client's JID. This will be updated asynchronously when - // resource binding completes; at that time an iq stanza will - // be published on the In channel: - // jid + // This client's JID. This will be updated asynchronously by + // the time StartSession() returns. Jid JID password string socket net.Conn @@ -58,6 +56,7 @@ idMutex sync.Mutex nextId int64 handlers chan *stanzaHandler + inputControl chan int // Incoming XMPP stanzas from the server will be published on // this channel. Information which is only used by this // library to set up the XMPP stream will not appear here. @@ -108,6 +107,7 @@ cl.Jid = *jid cl.socket = tcp cl.handlers = make(chan *stanzaHandler, 1) + cl.inputControl = make(chan int) // Start the transport handler, initially unencrypted. tlsr, tlsw := cl.startTransport() @@ -119,7 +119,7 @@ // Start the XMPP stream handler which filters stream-level // events and responds to them. clIn := cl.startStreamReader(xmlIn, cl.xmlOut) - clOut := startStreamWriter(cl.xmlOut) + clOut := cl.startStreamWriter(cl.xmlOut) // Initial handshake. hsOut := &stream{To: jid.Domain, Version: Version} @@ -162,9 +162,9 @@ return ch } -func startStreamWriter(xmlOut chan<- interface{}) chan<- Stanza { +func (cl *Client) startStreamWriter(xmlOut chan<- interface{}) chan<- Stanza { ch := make(chan Stanza) - go writeStream(xmlOut, ch) + go writeStream(xmlOut, ch, cl.inputControl) return ch } @@ -231,6 +231,13 @@ return fmt.Sprintf("id_%d", id) } +// bindDone is called when we've finished resource binding (and all +// the negotiations that precede it). Now we can start accepting +// traffic from the app. +func (cl *Client) bindDone() { + cl.inputControl <- 1 +} + // Start an XMPP session. This should typically be done immediately // after creating the new Client. Once the session has been // established, pr will be sent as an initial presence; nil means