From 133b91b9f0bb05ff454f938c767e93fa0aa0cbe3 Mon Sep 17 00:00:00 2001 From: Jeremy Latt Date: Sun, 9 Feb 2014 12:13:09 -0800 Subject: [PATCH] idle/quit timeout --- irc/client.go | 32 +++++++++++++++++++++++++++++++- irc/constants.go | 12 ++++++++++-- irc/reply.go | 8 ++++---- irc/server.go | 3 ++- 4 files changed, 47 insertions(+), 8 deletions(-) diff --git a/irc/client.go b/irc/client.go index 02de0bc0..2d5a9e81 100644 --- a/irc/client.go +++ b/irc/client.go @@ -8,7 +8,6 @@ import ( ) type Client struct { - atime time.Time away bool channels ChannelSet conn net.Conn @@ -22,6 +21,8 @@ type Client struct { server *Server serverPass bool username string + idleTimer *time.Timer + quitTimer *time.Timer } func NewClient(server *Server, conn net.Conn) *Client { @@ -40,9 +41,38 @@ func NewClient(server *Server, conn net.Conn) *Client { go client.readConn(read) go client.writeConn(write, replies) + client.Touch() return client } +func (client *Client) Touch() { + if client.quitTimer != nil { + client.quitTimer.Stop() + } + if client.idleTimer == nil { + client.idleTimer = time.AfterFunc(IDLE_TIMEOUT, client.Idle) + } else { + client.idleTimer.Reset(IDLE_TIMEOUT) + } +} + +func (client *Client) Idle() { + if client.quitTimer == nil { + client.quitTimer = time.AfterFunc(QUIT_TIMEOUT, client.Quit) + } else { + client.quitTimer.Reset(QUIT_TIMEOUT) + } + client.Reply(RplPing(client.server, client)) +} + +func (client *Client) Quit() { + msg := &QuitCommand{ + message: "connection timeout", + } + msg.SetClient(client) + client.server.commands <- msg +} + func (c *Client) readConn(recv <-chan string) { for str := range recv { m, err := ParseCommand(str) diff --git a/irc/constants.go b/irc/constants.go index cb8dfc89..cdf2635c 100644 --- a/irc/constants.go +++ b/irc/constants.go @@ -2,6 +2,7 @@ package irc import ( "errors" + "time" ) var ( @@ -14,8 +15,14 @@ var ( ) const ( - VERSION = "ergonomadic-1" - CRLF = "\r\n" + VERSION = "ergonomadic-1" + CRLF = "\r\n" + MAX_REPLY_LEN = 512 - len(CRLF) + + // how long before a client is considered idle + IDLE_TIMEOUT = time.Minute + // how long after idle before a client is kicked + QUIT_TIMEOUT = time.Minute // numeric codes RPL_WELCOME = 1 @@ -161,6 +168,7 @@ const ( RPL_JOIN = "JOIN" RPL_NICK = "NICK" RPL_PART = "PART" + RPL_PING = "PING" RPL_PONG = "PONG" RPL_PRIVMSG = "PRIVMSG" RPL_QUIT = "QUIT" diff --git a/irc/reply.go b/irc/reply.go index 6d7206ed..24e0d8db 100644 --- a/irc/reply.go +++ b/irc/reply.go @@ -6,10 +6,6 @@ import ( "time" ) -const ( - MAX_REPLY_LEN = 510 // 512 - CRLF -) - func joinedLen(names []string) int { var l = len(names) - 1 // " " between names for _, name := range names { @@ -139,6 +135,10 @@ func RplPart(client *Client, channel *Channel, message string) Reply { return NewStringReply(client, RPL_PART, "%s :%s", channel.name, message) } +func RplPing(server *Server, target Identifier) Reply { + return NewStringReply(server, RPL_PING, target.Nick()) +} + func RplPong(server *Server, client *Client) Reply { return NewStringReply(server, RPL_PONG, client.Nick()) } diff --git a/irc/server.go b/irc/server.go index 76bbcf45..b6e2772d 100644 --- a/irc/server.go +++ b/irc/server.go @@ -47,7 +47,8 @@ func (server *Server) receiveCommands(commands <-chan Command) { log.Printf("%s → %s : %s", command.Client(), server, command) } client := command.Client() - client.atime = time.Now() + client.Touch() + if !client.serverPass { if server.password == "" { client.serverPass = true