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:
parent
2bc1b952a0
commit
9e471b5b5d
@ -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()
|
|
||||||
}
|
}
|
||||||
|
@ -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 {
|
||||||
|
@ -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
|
||||||
//
|
//
|
||||||
|
Loading…
Reference in New Issue
Block a user