diff --git a/irc.go b/irc.go index 0aec775..7addef9 100644 --- a/irc.go +++ b/irc.go @@ -23,7 +23,21 @@ func (irc *Connection) readLoop() { br := bufio.NewReaderSize(irc.socket, 512) for { + // Set a read deadline based on the combined timeout and ping frequency + // We should ALWAYS have received a response from the server within the timeout + // after our own pings + if irc.socket != nil { + irc.socket.SetReadDeadline(time.Now().Add(irc.Timeout + irc.PingFreq)) + } + msg, err := br.ReadString('\n') + + // We got past our blocking read, so bin timeout + if irc.socket != nil { + var zero time.Time + irc.socket.SetReadDeadline(zero) + } + if err != nil { irc.Error <- err break @@ -77,7 +91,16 @@ func (irc *Connection) writeLoop() { if irc.Debug { irc.log.Printf("--> %s\n", b) } + + // Set a write deadline based on the time out + irc.socket.SetWriteDeadline(time.Now().Add(irc.Timeout)) + _, err := irc.socket.Write([]byte(b)) + + // Past blocking write, bin timeout + var zero time.Time + irc.socket.SetWriteDeadline(zero) + if err != nil { irc.Error <- err break @@ -86,19 +109,19 @@ func (irc *Connection) writeLoop() { irc.writerExit <- true } -//Pings the server if we have not recived any messages for 5 minutes +//Pings the server if we have not received any messages for 5 minutes func (irc *Connection) pingLoop() { - ticker := time.NewTicker(1 * time.Minute) //Tick every minute. - ticker2 := time.NewTicker(15 * time.Minute) //Tick every 15 minutes. + ticker := time.NewTicker(1 * time.Minute) // Tick every minute for monitoring + ticker2 := time.NewTicker(irc.PingFreq) // Tick at the ping frequency. for { select { case <-ticker.C: - //Ping if we haven't received anything from the server within 4 minutes - if time.Since(irc.lastMessage) >= (4 * time.Minute) { + //Ping if we haven't received anything from the server within the keep alive period + if time.Since(irc.lastMessage) >= irc.KeepAlive { irc.SendRawf("PING %d", time.Now().UnixNano()) } case <-ticker2.C: - //Ping every 15 minutes. + //Ping at the ping frequency irc.SendRawf("PING %d", time.Now().UnixNano()) //Try to recapture nickname if it's not as configured. if irc.nick != irc.nickcurrent { @@ -192,6 +215,10 @@ func (irc *Connection) Disconnect() { <-irc.pingerExit irc.socket.Close() irc.socket = nil + if irc.netsock != nil { + irc.netsock.Close() + irc.netsock = nil + } } func (irc *Connection) Reconnect() error { @@ -204,9 +231,11 @@ func (irc *Connection) Connect(server string) error { var err error if irc.UseTLS { - irc.socket, err = tls.Dial("tcp", irc.server, irc.TLSConfig) + if irc.netsock, err = net.DialTimeout("tcp", irc.server, irc.Timeout); err == nil { + irc.socket = tls.Client(irc.netsock, irc.TLSConfig) + } } else { - irc.socket, err = net.Dial("tcp", irc.server) + irc.socket, err = net.DialTimeout("tcp", irc.server, irc.Timeout) } if err != nil { return err @@ -238,6 +267,10 @@ func IRC(nick, user string) *Connection { writerExit: make(chan bool), pingerExit: make(chan bool), endping: make(chan bool), + Version: VERSION, + KeepAlive: 4 * time.Minute, + Timeout: 1 * time.Minute, + PingFreq: 15 * time.Minute, } irc.setupCallbacks() return irc diff --git a/irc_callback.go b/irc_callback.go index 169b5cb..7b06e95 100644 --- a/irc_callback.go +++ b/irc_callback.go @@ -84,7 +84,7 @@ func (irc *Connection) setupCallbacks() { //Version handler irc.AddCallback("CTCP_VERSION", func(e *Event) { - irc.SendRawf("NOTICE %s :\x01VERSION %s\x01", e.Nick, VERSION) + irc.SendRawf("NOTICE %s :\x01VERSION %s\x01", e.Nick, irc.Version) }) irc.AddCallback("CTCP_USERINFO", func(e *Event) { diff --git a/irc_struct.go b/irc_struct.go index fa4bb2b..f95cc8e 100644 --- a/irc_struct.go +++ b/irc_struct.go @@ -17,8 +17,13 @@ type Connection struct { Password string UseTLS bool TLSConfig *tls.Config + Version string + Timeout time.Duration + PingFreq time.Duration + KeepAlive time.Duration socket net.Conn + netsock net.Conn pread, pwrite chan string readerExit, writerExit, pingerExit chan bool endping chan bool