From ad513da4862e543f86ef237f6b3deb88dad51a15 Mon Sep 17 00:00:00 2001 From: Jeremy Latt Date: Thu, 20 Feb 2014 13:03:33 -0800 Subject: [PATCH] do hostname lookups in the client read thread --- irc/client.go | 71 ++++++++++++++++++++++++++++++++++++--------------- irc/server.go | 42 ++++++++++++------------------ irc/socket.go | 25 +++++++++++++----- 3 files changed, 86 insertions(+), 52 deletions(-) diff --git a/irc/client.go b/irc/client.go index 0da48c85..2d7a5d0f 100644 --- a/irc/client.go +++ b/irc/client.go @@ -12,6 +12,18 @@ func IsNickname(nick string) bool { return NicknameExpr.MatchString(nick) } +type HostnameLookup struct { + client *Client + hostname string +} + +func NewHostnameLookup(client *Client, ipAddr string) *HostnameLookup { + return &HostnameLookup{ + client: client, + hostname: LookupHostname(ipAddr), + } +} + type Client struct { atime time.Time awayMessage string @@ -23,6 +35,7 @@ type Client struct { hostname string idleTimer *time.Timer loginTimer *time.Timer + lookups chan string nick string phase Phase quitTimer *time.Timer @@ -33,14 +46,14 @@ type Client struct { username string } -func NewClient(server *Server, conn net.Conn, hostname string) *Client { +func NewClient(server *Server, conn net.Conn) *Client { now := time.Now() client := &Client{ atime: now, channels: make(ChannelSet), ctime: now, flags: make(map[UserMode]bool), - hostname: hostname, + lookups: make(chan string), phase: server.InitPhase(), server: server, socket: NewSocket(conn), @@ -48,6 +61,7 @@ func NewClient(server *Server, conn net.Conn, hostname string) *Client { } client.loginTimer = time.AfterFunc(LOGIN_TIMEOUT, client.connectionTimeout) + go client.LookupHostname(IPString(conn.RemoteAddr())) go client.readCommands() go client.writeReplies() @@ -59,28 +73,39 @@ func NewClient(server *Server, conn net.Conn, hostname string) *Client { // func (client *Client) readCommands() { - for { - line, err := client.socket.Read() - if err != nil { - break - } - msg, err := ParseCommand(line) - if err != nil { - switch err { - case NotEnoughArgsError: - parts := strings.SplitN(line, " ", 2) - client.ErrNeedMoreParams(parts[0]) - } - continue - } + done := false + for !done { + select { + case ipAddr := <-client.lookups: + client.server.hostnames <- NewHostnameLookup(client, ipAddr) - msg.SetClient(client) - client.server.commands <- msg + case line := <-client.socket.Read(): + if line == EOF { + done = true + break + } + msg, err := ParseCommand(line) + if err != nil { + switch err { + case NotEnoughArgsError: + parts := strings.SplitN(line, " ", 2) + client.ErrNeedMoreParams(parts[0]) + } + continue + } + + msg.SetClient(client) + client.server.commands <- msg + } } client.connectionClosed() } +func (client *Client) LookupHostname(ipAddr string) { + client.lookups <- ipAddr +} + func (client *Client) connectionClosed() { msg := &QuitCommand{ message: "connection closed", @@ -95,11 +120,18 @@ func (client *Client) connectionClosed() { func (client *Client) writeReplies() { for reply := range client.replies { + if reply == EOF { + break + } if client.socket.Write(reply) != nil { break } } client.socket.Close() + + for _ = range client.replies { + // discard + } } // @@ -161,8 +193,6 @@ func (client *Client) Register() { func (client *Client) destroy() { // clean up self - close(client.replies) - client.loginTimer.Stop() if client.idleTimer != nil { @@ -275,6 +305,7 @@ func (client *Client) Quit(message string) { } client.replies <- RplError(client.server, "connection closed") + client.replies <- EOF client.hasQuit = true friends := client.Friends() diff --git a/irc/server.go b/irc/server.go index 94cc2659..adc9fdc9 100644 --- a/irc/server.go +++ b/irc/server.go @@ -13,31 +13,16 @@ import ( "time" ) -type ConnData struct { - conn net.Conn - hostname string -} - -func NewConnData(conn net.Conn) *ConnData { - return &ConnData{ - conn: conn, - } -} - -func (data *ConnData) LookupHostname(newConns chan<- *ConnData) { - data.hostname = AddrLookupHostname(data.conn.RemoteAddr()) - newConns <- data -} - type Server struct { channels ChannelNameMap clients ClientNameMap commands chan Command - newConns chan *ConnData ctime time.Time + hostnames chan *HostnameLookup idle chan *Client motdFile string name string + newConns chan net.Conn operators map[string]string password string } @@ -46,12 +31,13 @@ func NewServer(config *Config) *Server { server := &Server{ channels: make(ChannelNameMap), clients: make(ClientNameMap), - commands: make(chan Command), - newConns: make(chan *ConnData), + commands: make(chan Command, 16), ctime: time.Now(), - idle: make(chan *Client), + hostnames: make(chan *HostnameLookup, 16), + idle: make(chan *Client, 16), motdFile: config.MOTD, name: config.Name, + newConns: make(chan net.Conn, 16), operators: make(map[string]string), password: config.Password, } @@ -70,8 +56,15 @@ func NewServer(config *Config) *Server { func (server *Server) ReceiveCommands() { for { select { - case data := <-server.newConns: - NewClient(server, data.conn, data.hostname) + case conn := <-server.newConns: + NewClient(server, conn) + + case lookup := <-server.hostnames: + if DEBUG_SERVER { + log.Printf("%s setting hostname of %s to %s", + server, lookup.client, lookup.hostname) + } + lookup.client.hostname = lookup.hostname case client := <-server.idle: client.Idle() @@ -168,7 +161,7 @@ func (s *Server) listen(config ListenerConfig) { log.Printf("%s accept: %s", s, conn.RemoteAddr()) } - go NewConnData(conn).LookupHostname(s.newConns) + s.newConns <- conn } } @@ -270,8 +263,7 @@ func (s *Server) Nick() string { // func (msg *ProxyCommand) HandleAuthServer(server *Server) { - client := msg.Client() - client.hostname = LookupHostname(msg.sourceIP) + go msg.Client().LookupHostname(msg.sourceIP) } func (msg *CapCommand) HandleAuthServer(server *Server) { diff --git a/irc/socket.go b/irc/socket.go index a456358b..a69d4760 100644 --- a/irc/socket.go +++ b/irc/socket.go @@ -2,19 +2,22 @@ package irc import ( "bufio" + "io" "log" "net" "strings" ) const ( - R = '→' - W = '←' + R = '→' + W = '←' + EOF = "" ) type Socket struct { closed bool conn net.Conn + read chan string reader *bufio.Reader writer *bufio.Writer } @@ -22,10 +25,13 @@ type Socket struct { func NewSocket(conn net.Conn) *Socket { socket := &Socket{ conn: conn, + read: make(chan string), reader: bufio.NewReader(conn), writer: bufio.NewWriter(conn), } + go socket.readLines() + return socket } @@ -44,9 +50,9 @@ func (socket *Socket) Close() { } } -func (socket *Socket) Read() (line string, err error) { - for len(line) == 0 { - line, err = socket.reader.ReadString('\n') +func (socket *Socket) readLines() { + for { + line, err := socket.reader.ReadString('\n') if socket.isError(err, R) { break } @@ -58,8 +64,13 @@ func (socket *Socket) Read() (line string, err error) { if DEBUG_NET { log.Printf("%s → %s", socket, line) } + socket.read <- line } - return + close(socket.read) +} + +func (socket *Socket) Read() <-chan string { + return socket.read } func (socket *Socket) Write(lines ...string) (err error) { @@ -93,7 +104,7 @@ func (socket *Socket) WriteLine(line string) (err error) { func (socket *Socket) isError(err error, dir rune) bool { if err != nil { - if DEBUG_NET { + if DEBUG_NET && (err != io.EOF) { log.Printf("%s %c error: %s", socket, dir, err) } return true