3
0
mirror of https://github.com/ergochat/ergo.git synced 2024-12-22 18:52:41 +01:00

solve quit/connection close race

This commit is contained in:
Jeremy Latt 2014-02-18 13:25:21 -08:00
parent 2bc1b952a0
commit 9e471b5b5d
3 changed files with 77 additions and 39 deletions

View File

@ -19,6 +19,7 @@ type Client struct {
ctime time.Time ctime time.Time
flags map[UserMode]bool flags map[UserMode]bool
friends map[*Client]uint friends map[*Client]uint
hasQuit bool
hops uint hops uint
hostname string hostname string
idleTimer *time.Timer idleTimer *time.Timer
@ -46,12 +47,16 @@ func NewClient(server *Server, conn net.Conn) *Client {
socket: NewSocket(conn), socket: NewSocket(conn),
} }
client.loginTimer = time.AfterFunc(LOGIN_TIMEOUT, client.connectionClosed) client.loginTimer = time.AfterFunc(LOGIN_TIMEOUT, client.connectionTimeout)
go client.readCommands() go client.readCommands()
return client return client
} }
//
// socket read gorountine
//
func (client *Client) readCommands() { func (client *Client) readCommands() {
for line := range client.socket.Read() { for line := range client.socket.Read() {
msg, err := ParseCommand(line) msg, err := ParseCommand(line)
@ -68,12 +73,46 @@ func (client *Client) readCommands() {
client.server.commands <- msg client.server.commands <- msg
} }
client.server.toDestroy <- client client.connectionClosed()
}
func (client *Client) connectionClosed() {
msg := &QuitCommand{
message: "connection closed",
}
msg.SetClient(client)
client.server.commands <- msg
}
//
// idle timer goroutine
//
func (client *Client) connectionIdle() {
client.server.idle <- client
}
//
// quit timer goroutine
//
func (client *Client) connectionTimeout() {
msg := &QuitCommand{
message: "connection timeout",
}
msg.SetClient(client)
client.server.commands <- msg
}
//
// server goroutine
//
func (client *Client) Active() {
client.atime = time.Now()
} }
func (client *Client) Touch() { func (client *Client) Touch() {
client.atime = time.Now()
if client.quitTimer != nil { if client.quitTimer != nil {
client.quitTimer.Stop() client.quitTimer.Stop()
} }
@ -95,24 +134,11 @@ func (client *Client) Idle() {
} }
} }
func (client *Client) connectionIdle() { func (client *Client) Register() {
client.server.idle <- client client.phase = Normal
} client.loginTimer.Stop()
client.AddFriend(client)
func (client *Client) connectionTimeout() { client.Touch()
msg := &QuitCommand{
message: "connection timeout",
}
msg.SetClient(client)
client.server.commands <- msg
}
func (client *Client) connectionClosed() {
msg := &QuitCommand{
message: "connection closed",
}
msg.SetClient(client)
client.server.commands <- msg
} }
func (client *Client) Destroy() { func (client *Client) Destroy() {
@ -225,6 +251,13 @@ func (client *Client) ChangeNickname(nickname string) {
} }
func (client *Client) Quit(message string) { func (client *Client) Quit(message string) {
if client.hasQuit {
return
}
client.hasQuit = true
client.Reply(RplError(client.server, client.Nick()))
client.Destroy()
if len(client.friends) > 0 { if len(client.friends) > 0 {
reply := RplQuit(client, message) reply := RplQuit(client, message)
for friend := range client.friends { for friend := range client.friends {
@ -234,7 +267,4 @@ func (client *Client) Quit(message string) {
friend.Reply(reply) friend.Reply(reply)
} }
} }
client.Reply(RplError(client.server, client))
client.socket.Close()
} }

View File

@ -238,8 +238,8 @@ func RplQuit(client *Client, message string) Reply {
return NewStringReply(client, QUIT, ":%s", message) return NewStringReply(client, QUIT, ":%s", message)
} }
func RplError(server *Server, target Identifier) Reply { func RplError(server *Server, message string) Reply {
return NewStringReply(server, ERROR, target.Nick()) return NewStringReply(server, ERROR, message)
} }
func RplInviteMsg(channel *Channel, inviter *Client) Reply { func RplInviteMsg(channel *Channel, inviter *Client) Reply {

View File

@ -24,7 +24,6 @@ type Server struct {
name string name string
operators map[string]string operators map[string]string
password string password string
toDestroy chan *Client
} }
func NewServer(config *Config) *Server { func NewServer(config *Config) *Server {
@ -39,7 +38,6 @@ func NewServer(config *Config) *Server {
name: config.Name, name: config.Name,
operators: make(map[string]string), operators: make(map[string]string),
password: config.Password, password: config.Password,
toDestroy: make(chan *Client),
} }
for _, opConf := range config.Operators { for _, opConf := range config.Operators {
@ -59,9 +57,6 @@ func (server *Server) ReceiveCommands() {
case conn := <-server.conns: case conn := <-server.conns:
NewClient(server, conn) NewClient(server, conn)
case client := <-server.toDestroy:
client.Destroy()
case client := <-server.idle: case client := <-server.idle:
client.Idle() client.Idle()
@ -75,7 +70,7 @@ func (server *Server) ReceiveCommands() {
case Authorization: case Authorization:
authCmd, ok := cmd.(AuthServerCommand) authCmd, ok := cmd.(AuthServerCommand)
if !ok { if !ok {
client.socket.Close() client.Quit("unexpected command")
continue continue
} }
authCmd.HandleAuthServer(server) authCmd.HandleAuthServer(server)
@ -83,7 +78,7 @@ func (server *Server) ReceiveCommands() {
case Registration: case Registration:
regCmd, ok := cmd.(RegServerCommand) regCmd, ok := cmd.(RegServerCommand)
if !ok { if !ok {
client.socket.Close() client.Quit("unexpected command")
continue continue
} }
regCmd.HandleRegServer(server) regCmd.HandleRegServer(server)
@ -94,7 +89,15 @@ func (server *Server) ReceiveCommands() {
client.Reply(ErrUnknownCommand(server, cmd.Code())) client.Reply(ErrUnknownCommand(server, cmd.Code()))
continue continue
} }
client.Touch() switch srvCmd.(type) {
case *PingCommand, *PongCommand:
client.Touch()
case *QuitCommand:
// no-op
default:
client.Active()
client.Touch()
}
srvCmd.HandleServer(server) srvCmd.HandleServer(server)
} }
} }
@ -184,10 +187,7 @@ func (s *Server) GenerateGuestNick() string {
func (s *Server) tryRegister(c *Client) { func (s *Server) tryRegister(c *Client) {
if c.HasNick() && c.HasUsername() { if c.HasNick() && c.HasUsername() {
c.phase = Normal c.Register()
c.loginTimer.Stop()
c.AddFriend(c)
c.Reply(RplWelcome(s, c)) c.Reply(RplWelcome(s, c))
c.Reply(RplYourHost(s)) c.Reply(RplYourHost(s))
c.Reply(RplCreated(s)) c.Reply(RplCreated(s))
@ -270,6 +270,10 @@ func (m *PassCommand) HandleAuthServer(s *Server) {
client.phase = Registration client.phase = Registration
} }
func (msg *QuitCommand) HandleAuthServer(server *Server) {
msg.Client().Quit(msg.message)
}
// //
// registration commands // registration commands
// //
@ -319,6 +323,10 @@ func (msg *UserCommand) HandleRegServer2(server *Server) {
server.tryRegister(client) server.tryRegister(client)
} }
func (msg *QuitCommand) HandleRegServer(server *Server) {
msg.Client().Quit(msg.message)
}
// //
// normal commands // normal commands
// //