From 0bf968e19e6d7de892115662823ae9721f46deed Mon Sep 17 00:00:00 2001 From: Jeremy Latt Date: Sun, 23 Feb 2014 17:04:24 -0800 Subject: [PATCH] move command parsing and hostname lookups into the socket routine --- irc/client.go | 103 +++--------------------------------------------- irc/commands.go | 14 +++---- irc/server.go | 22 +++++------ irc/socket.go | 49 ++++++++++++----------- irc/types.go | 1 - 5 files changed, 49 insertions(+), 140 deletions(-) diff --git a/irc/client.go b/irc/client.go index cb72e88c..2bfa7de1 100644 --- a/irc/client.go +++ b/irc/client.go @@ -4,7 +4,6 @@ import ( "fmt" "log" "net" - "strings" "time" ) @@ -12,18 +11,6 @@ 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 @@ -35,12 +22,10 @@ type Client struct { hostname string idleTimer *time.Timer loginTimer *time.Timer - lookups chan string nick string phase Phase quitTimer *time.Timer realname string - replies chan string server *Server socket *Socket username string @@ -53,83 +38,15 @@ func NewClient(server *Server, conn net.Conn) *Client { channels: make(ChannelSet), ctime: now, flags: make(map[UserMode]bool), - lookups: make(chan string), phase: server.InitPhase(), server: server, - socket: NewSocket(conn), - replies: make(chan string, 16), } - + client.socket = NewSocket(conn, client, server.commands) client.loginTimer = time.AfterFunc(LOGIN_TIMEOUT, client.connectionTimeout) - go client.LookupHostname(IPString(conn.RemoteAddr())) - go client.readCommands() - go client.writeReplies() return client } -// -// socket read gorountine -// - -func (client *Client) readCommands() { - done := false - for !done { - select { - case ipAddr := <-client.lookups: - client.server.hostnames <- NewHostnameLookup(client, ipAddr) - - 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", - } - msg.SetClient(client) - client.server.commands <- msg -} - -// -// reply writing goroutine -// - -func (client *Client) writeReplies() { - for reply := range client.replies { - if reply == EOF { - break - } - if client.socket.Write(reply) != nil { - break - } - } - client.socket.Close() -} - // // idle timer goroutine // @@ -143,11 +60,7 @@ func (client *Client) connectionIdle() { // func (client *Client) connectionTimeout() { - msg := &QuitCommand{ - message: "connection timeout", - } - msg.SetClient(client) - client.server.commands <- msg + client.server.timeout <- client } // @@ -214,10 +127,10 @@ func (client *Client) destroy() { client.quitTimer = nil } - client.lookups = nil - client.replies = nil - client.server = nil + client.socket.Close() + client.socket = nil + client.server = nil if DEBUG_CLIENT { log.Printf("%s: destroyed", client) @@ -307,10 +220,7 @@ func (client *Client) ChangeNickname(nickname string) { } func (client *Client) Reply(reply string) { - if client.hasQuit { - return - } - client.replies <- reply + client.socket.Write(reply) } func (client *Client) Quit(message string) { @@ -319,7 +229,6 @@ func (client *Client) Quit(message string) { } client.Reply(RplError("connection closed")) - client.Reply(EOF) client.hasQuit = true friends := client.Friends() diff --git a/irc/commands.go b/irc/commands.go index 7d255807..c90707af 100644 --- a/irc/commands.go +++ b/irc/commands.go @@ -56,8 +56,8 @@ func (command *BaseCommand) Client() *Client { return command.client } -func (command *BaseCommand) SetClient(c *Client) { - command.client = c +func (command *BaseCommand) SetClient(client *Client) { + command.client = client } func (command *BaseCommand) Code() StringCode { @@ -68,10 +68,6 @@ func (command *BaseCommand) SetCode(code StringCode) { command.code = code } -func (command *BaseCommand) Source() Identifier { - return command.Client() -} - func ParseCommand(line string) (cmd editableCommand, err error) { code, args := parseLine(line) constructor := parseCommandFuncs[code] @@ -362,7 +358,7 @@ type PartCommand struct { func (cmd *PartCommand) Message() string { if cmd.message == "" { - return cmd.Source().Nick() + return cmd.Client().Nick() } return cmd.message } @@ -701,6 +697,7 @@ type ProxyCommand struct { destIP string sourcePort string destPort string + hostname string // looked up in socket thread } func (msg *ProxyCommand) String() string { @@ -717,6 +714,7 @@ func NewProxyCommand(args []string) (editableCommand, error) { destIP: args[2], sourcePort: args[3], destPort: args[4], + hostname: LookupHostname(args[1]), }, nil } @@ -801,7 +799,7 @@ type KickCommand struct { func (msg *KickCommand) Comment() string { if msg.comment == "" { - return msg.Source().Nick() + return msg.Client().Nick() } return msg.comment } diff --git a/irc/server.go b/irc/server.go index 2772d681..64ee425c 100644 --- a/irc/server.go +++ b/irc/server.go @@ -21,13 +21,13 @@ type Server struct { clients ClientNameMap commands chan Command ctime time.Time - hostnames chan *HostnameLookup idle chan *Client motdFile string name string newConns chan net.Conn operators map[string]string password string + timeout chan *Client } func NewServer(config *Config) *Server { @@ -36,13 +36,13 @@ func NewServer(config *Config) *Server { clients: make(ClientNameMap), commands: make(chan Command, 16), ctime: time.Now(), - 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, + timeout: make(chan *Client), } for _, opConf := range config.Operators { @@ -62,16 +62,12 @@ func (server *Server) ReceiveCommands() { 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() + case client := <-server.timeout: + client.Quit("connection timeout") + case cmd := <-server.commands: client := cmd.Client() if DEBUG_SERVER { @@ -255,7 +251,7 @@ func (s *Server) Nick() string { // func (msg *ProxyCommand) HandleAuthServer(server *Server) { - go msg.Client().LookupHostname(msg.sourceIP) + msg.Client().hostname = msg.hostname } func (msg *CapCommand) HandleAuthServer(server *Server) { @@ -267,7 +263,7 @@ func (m *PassCommand) HandleAuthServer(s *Server) { if s.password != m.password { client.ErrPasswdMismatch() - client.socket.Close() + client.Quit("bad password") return } @@ -282,6 +278,10 @@ func (msg *QuitCommand) HandleAuthServer(server *Server) { // registration commands // +func (msg *ProxyCommand) HandleRegServer(server *Server) { + msg.Client().hostname = msg.hostname +} + func (msg *CapCommand) HandleRegServer(server *Server) { // TODO } diff --git a/irc/socket.go b/irc/socket.go index fef2faca..767b7d30 100644 --- a/irc/socket.go +++ b/irc/socket.go @@ -17,20 +17,20 @@ const ( type Socket struct { closed bool conn net.Conn - read chan string reader *bufio.Reader + client *Client writer *bufio.Writer } -func NewSocket(conn net.Conn) *Socket { +func NewSocket(conn net.Conn, client *Client, commands chan<- Command) *Socket { socket := &Socket{ conn: conn, - read: make(chan string), reader: bufio.NewReader(conn), + client: client, writer: bufio.NewWriter(conn), } - go socket.readLines() + go socket.readLines(commands) return socket } @@ -50,13 +50,18 @@ func (socket *Socket) Close() { } } -func (socket *Socket) readLines() { +func (socket *Socket) readLines(commands chan<- Command) { + hostnameLookup := &ProxyCommand{ + hostname: AddrLookupHostname(socket.conn.RemoteAddr()), + } + hostnameLookup.SetClient(socket.client) + commands <- hostnameLookup + for { line, err := socket.reader.ReadString('\n') if socket.isError(err, R) { break } - line = strings.TrimRight(line, "\r\n") if len(line) == 0 { continue @@ -64,29 +69,27 @@ func (socket *Socket) readLines() { if DEBUG_NET { log.Printf("%s → %s", socket, line) } - socket.read <- line + + msg, err := ParseCommand(line) + if err != nil { + // TODO error messaging to client + continue + } + msg.SetClient(socket.client) + commands <- msg } - close(socket.read) + + msg := &QuitCommand{ + message: "connection closed", + } + msg.SetClient(socket.client) + commands <- msg } -func (socket *Socket) Read() <-chan string { - return socket.read -} - -func (socket *Socket) Write(lines ...string) (err error) { +func (socket *Socket) Write(line string) (err error) { if socket.closed { return io.EOF } - for _, line := range lines { - err = socket.WriteLine(line) - if err != nil { - break - } - } - return -} - -func (socket *Socket) WriteLine(line string) (err error) { if _, err = socket.writer.WriteString(line); socket.isError(err, W) { return } diff --git a/irc/types.go b/irc/types.go index e8991f4d..0f15d314 100644 --- a/irc/types.go +++ b/irc/types.go @@ -176,7 +176,6 @@ type Replier interface { type Command interface { Code() StringCode Client() *Client - Source() Identifier } type ServerCommand interface {