3
0
mirror of https://github.com/ergochat/ergo.git synced 2024-11-10 22:19:31 +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
flags map[UserMode]bool
friends map[*Client]uint
hasQuit bool
hops uint
hostname string
idleTimer *time.Timer
@ -46,12 +47,16 @@ func NewClient(server *Server, conn net.Conn) *Client {
socket: NewSocket(conn),
}
client.loginTimer = time.AfterFunc(LOGIN_TIMEOUT, client.connectionClosed)
client.loginTimer = time.AfterFunc(LOGIN_TIMEOUT, client.connectionTimeout)
go client.readCommands()
return client
}
//
// socket read gorountine
//
func (client *Client) readCommands() {
for line := range client.socket.Read() {
msg, err := ParseCommand(line)
@ -68,12 +73,46 @@ func (client *Client) readCommands() {
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() {
client.atime = time.Now()
if client.quitTimer != nil {
client.quitTimer.Stop()
}
@ -95,24 +134,11 @@ func (client *Client) Idle() {
}
}
func (client *Client) connectionIdle() {
client.server.idle <- client
}
func (client *Client) connectionTimeout() {
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) Register() {
client.phase = Normal
client.loginTimer.Stop()
client.AddFriend(client)
client.Touch()
}
func (client *Client) Destroy() {
@ -225,6 +251,13 @@ func (client *Client) ChangeNickname(nickname 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 {
reply := RplQuit(client, message)
for friend := range client.friends {
@ -234,7 +267,4 @@ func (client *Client) Quit(message string) {
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)
}
func RplError(server *Server, target Identifier) Reply {
return NewStringReply(server, ERROR, target.Nick())
func RplError(server *Server, message string) Reply {
return NewStringReply(server, ERROR, message)
}
func RplInviteMsg(channel *Channel, inviter *Client) Reply {

View File

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