func (c *conn) getCookie() data.Cookie { var buf [8]byte if _, err := c.Rand().Read(buf[:]); err != nil { panic("Failed to read random bytes: " + err.Error()) } return data.Cookie(binary.LittleEndian.Uint64(buf[:])) }
func (s *ConnectionXmppSuite) Test_Next_returnsNothingIfTheInflightIsToAnotherReceiver(c *C) { mockIn := &mockConnIOReaderWriter{read: []byte("<client:iq xmlns:client='jabber:client' type='result' id='100000' from='*****@*****.**'></client:iq>")} conn := conn{ in: xml.NewDecoder(mockIn), inflights: make(map[data.Cookie]inflight), } cookie := data.Cookie(1048576) conn.inflights[cookie] = inflight{to: "*****@*****.**"} _, err := conn.Next() c.Assert(err, Equals, io.EOF) }
// Next reads stanzas from the server. If the stanza is a reply, it dispatches // it to the correct channel and reads the next message. Otherwise it returns // the stanza for processing. func (c *conn) Next() (stanza data.Stanza, err error) { for { if stanza.Name, stanza.Value, err = next(c); err != nil { return } if _, ok := stanza.Value.(*data.StreamClose); ok { log.Println("xmpp: received closing stream tag") go c.closeImmediately() return } if iq, ok := stanza.Value.(*data.ClientIQ); ok && (iq.Type == "result" || iq.Type == "error") { var cookieValue uint64 if cookieValue, err = strconv.ParseUint(iq.ID, 16, 64); err != nil { err = errors.New("xmpp: failed to parse id from iq: " + err.Error()) return } cookie := data.Cookie(cookieValue) c.lock.Lock() inflight, ok := c.inflights[cookie] c.lock.Unlock() if !ok { continue } if len(inflight.to) > 0 { // The reply must come from the address to // which we sent the request. if inflight.to != iq.From { continue } } else { // If there was no destination on the request // then the matching is more complex because // servers differ in how they construct the // reply. if len(iq.From) > 0 && iq.From != c.jid && iq.From != utils.RemoveResourceFromJid(c.jid) && iq.From != utils.DomainFromJid(c.jid) { continue } } c.lock.Lock() delete(c.inflights, cookie) c.lock.Unlock() inflight.replyChan <- stanza continue } return } }
func (s *ConnectionXmppSuite) Test_Next_continuesIfIqFromIsNotSimilarToJid(c *C) { mockIn := &mockConnIOReaderWriter{read: []byte("<client:iq xmlns:client='jabber:client' type='result' id='100000' from='*****@*****.**'></client:iq>")} inflights := make(map[data.Cookie]inflight) conn := conn{ in: xml.NewDecoder(mockIn), inflights: inflights, jid: "[email protected]/blah", } cookie := data.Cookie(1048576) conn.inflights[cookie] = inflight{} _, err := conn.Next() c.Assert(err, Equals, io.EOF) _, ok := conn.inflights[cookie] c.Assert(ok, Equals, true) }
func (s *SessionSuite) Test_watchTimeouts_cancelsTimedoutRequestsAndForgetsAboutThem(c *C) { now := time.Now() timeouts := map[data.Cookie]time.Time{ data.Cookie(1): now.Add(-1 * time.Second), data.Cookie(2): now.Add(10 * time.Second), } sess := &session{ connStatus: CONNECTED, timeouts: timeouts, conn: xmpp.NewConn(nil, nil, ""), } go func() { <-time.After(1 * time.Second) sess.connStatus = DISCONNECTED }() sess.watchTimeout() c.Check(sess.timeouts, HasLen, 1) _, ok := sess.timeouts[data.Cookie(2)] c.Check(ok, Equals, true) }
func (s *ConnectionXmppSuite) Test_Next_removesIfThereIsNoFrom(c *C) { mockIn := &mockConnIOReaderWriter{read: []byte("<client:iq xmlns:client='jabber:client' type='result' id='100000'></client:iq>")} inflights := make(map[data.Cookie]inflight) conn := conn{ in: xml.NewDecoder(mockIn), inflights: inflights, } cookie := data.Cookie(1048576) reply := make(chan data.Stanza, 1) conn.inflights[cookie] = inflight{ replyChan: reply, } go func() { <-reply }() _, err := conn.Next() c.Assert(err, Equals, io.EOF) _, ok := conn.inflights[cookie] c.Assert(ok, Equals, false) }