1// Copyright 2013 The Go Authors. All rights reserved. 2// Use of this source code is governed by a BSD-style 3// license that can be found in the LICENSE file. 4 5// Package xmpp implements the XMPP IM protocol, as specified in RFC 6120 and 6// 6121. 7// Ping implements the XMPP extension Ping, as specified in xep-0199 8package xmpp 9 10import ( 11 "errors" 12 "log" 13 "time" 14 15 "github.com/coyim/coyim/xmpp/data" 16) 17 18// SendPing sends a Ping request. 19func (c *conn) SendPing() (reply <-chan data.Stanza, cookie data.Cookie, err error) { 20 //TODO: should not this be set when we send any message? Why would I send a ping 21 //just after sending a message? 22 c.lastPingRequest = time.Now() //TODO: this seems should not belong to Conn 23 return c.SendIQ("", "get", data.PingRequest{}) 24} 25 26// SendPingReply sends a reply to a Ping request. 27func (c *conn) SendPingReply(id string) error { 28 return c.SendIQReply("", "result", id, data.EmptyReply{}) 29} 30 31// ReceivePong update the timestamp for lastPongResponse, 32func (c *conn) ReceivePong() { 33 c.lastPongResponse = time.Now() //TODO: this seems should not belong to Conn 34} 35 36// ParsePong parse a reply of a Pong response. 37func ParsePong(reply data.Stanza) error { 38 iq, ok := reply.Value.(*data.ClientIQ) 39 if !ok { 40 return errors.New("xmpp: ping request resulted in tag of type " + reply.Name.Local) 41 } 42 switch iq.Type { 43 case "result": 44 return nil 45 case "error": 46 return errors.New("xmpp: ping request resulted in a error: " + iq.Error.Text) 47 default: 48 return errors.New("xmpp: ping request resulted in a unexpected type") 49 } 50} 51 52var ( 53 pingIterval = 10 * time.Second //should be 5 minutes at least, per spec 54 pingTimeout = 30 * time.Second 55 maxPingFailures = 2 56) 57 58func (c *conn) watchPings() { 59 tick := time.NewTicker(pingIterval) 60 defer tick.Stop() 61 failures := 0 62 63 for _ = range tick.C { 64 if c.closed { 65 return 66 } 67 68 pongReply, _, err := c.SendPing() 69 if err != nil { 70 return 71 } 72 73 select { 74 case <-time.After(pingTimeout): 75 // ping timed out 76 failures = failures + 1 77 case pongStanza, ok := <-pongReply: 78 if !ok { 79 // pong cancelled 80 continue 81 } 82 83 failures = 0 84 iq, ok := pongStanza.Value.(*data.ClientIQ) 85 if !ok { 86 //not the expected IQ 87 return 88 } 89 90 //TODO: check for <service-unavailable/> 91 if iq.Type == "error" { 92 //server does not support Ping 93 return 94 } 95 } 96 97 if failures < maxPingFailures { 98 continue 99 } 100 101 log.Println("xmpp: ping failures reached threshold of", maxPingFailures) 102 go c.sendStreamError(data.StreamError{ 103 DefinedCondition: data.ConnectionTimeout, 104 }) 105 106 return 107 } 108} 109