From 887f12cb31773cfd4ecae52995d2e70b2712d959 Mon Sep 17 00:00:00 2001 From: Jeremy Latt Date: Tue, 11 Feb 2014 15:00:19 -0800 Subject: [PATCH] get rid of channel abstraction for buffered socket io --- irc/client.go | 68 ++++++++++++++++++++++++++++++++++++++++----------- irc/net.go | 58 ------------------------------------------- irc/reply.go | 18 ++++++++------ irc/types.go | 2 +- 4 files changed, 65 insertions(+), 81 deletions(-) diff --git a/irc/client.go b/irc/client.go index 5ec2cdba..fcf73b50 100644 --- a/irc/client.go +++ b/irc/client.go @@ -1,9 +1,12 @@ package irc import ( + "bufio" "fmt" + "io" "log" "net" + "strings" "time" ) @@ -12,34 +15,36 @@ type Client struct { channels ChannelSet conn net.Conn hostname string + idleTimer *time.Timer invisible bool nick string operator bool + quitTimer *time.Timer realname string + recv *bufio.Reader registered bool replies chan<- Reply + send *bufio.Writer server *Server serverPass bool username string - idleTimer *time.Timer - quitTimer *time.Timer } func NewClient(server *Server, conn net.Conn) *Client { - read := StringReadChan(conn) - write := StringWriteChan(conn) replies := make(chan Reply) client := &Client{ channels: make(ChannelSet), conn: conn, hostname: AddrLookupHostname(conn.RemoteAddr()), + recv: bufio.NewReader(conn), replies: replies, + send: bufio.NewWriter(conn), server: server, } - go client.readConn(read) - go client.writeConn(write, replies) + go client.readConn() + go client.writeConn(replies) client.Touch() return client @@ -73,14 +78,26 @@ func (client *Client) Quit() { client.server.commands <- msg } -func (c *Client) readConn(recv <-chan string) { - for str := range recv { - m, err := ParseCommand(str) +func (c *Client) readConn() { + for { + line, err := c.recv.ReadString('\n') + if err != nil { + if err != io.EOF { + log.Printf("%s → %s error: %s", c.conn.RemoteAddr(), c.conn.LocalAddr(), err) + } + break + } + line = strings.TrimSpace(line) + if DEBUG_NET { + log.Printf("%s → %s %s", c.conn.RemoteAddr(), c.conn.LocalAddr(), line) + } + + m, err := ParseCommand(line) if err != nil { if err == NotEnoughArgsError { - c.Reply(ErrNeedMoreParams(c.server, str)) + c.Reply(ErrNeedMoreParams(c.server, line)) } else { - c.Reply(ErrUnknownCommand(c.server, str)) + c.Reply(ErrUnknownCommand(c.server, line)) } continue } @@ -90,12 +107,35 @@ func (c *Client) readConn(recv <-chan string) { } } -func (c *Client) writeConn(write chan<- string, replies <-chan Reply) { +func (client *Client) maybeLogWriteError(err error) bool { + if err != nil { + if err != io.EOF { + log.Printf("%s ← %s error: %s", client.conn.RemoteAddr(), client.conn.LocalAddr(), err) + } + return true + } + return false +} + +func (client *Client) writeConn(replies <-chan Reply) { for reply := range replies { if DEBUG_CLIENT { - log.Printf("%s ← %s %s", c, reply.Source(), reply) + log.Printf("%s ← %s %s", client, reply.Source(), reply) + } + for _, str := range reply.Format(client) { + if DEBUG_NET { + log.Printf("%s ← %s %s", client.conn.RemoteAddr(), client.conn.LocalAddr(), str) + } + if _, err := client.send.WriteString(str); client.maybeLogWriteError(err) { + break + } + if _, err := client.send.WriteString(CRLF); client.maybeLogWriteError(err) { + break + } + if err := client.send.Flush(); client.maybeLogWriteError(err) { + break + } } - reply.Format(c, write) } } diff --git a/irc/net.go b/irc/net.go index 30c7ab49..b0eebf78 100644 --- a/irc/net.go +++ b/irc/net.go @@ -1,68 +1,10 @@ package irc import ( - "bufio" - "io" - "log" "net" "strings" ) -// Adapt `net.Conn` to a `chan string`. -func StringReadChan(conn net.Conn) <-chan string { - ch := make(chan string) - reader := bufio.NewReader(conn) - go func() { - for { - line, err := reader.ReadString('\n') - if err != nil { - if err != io.EOF { - log.Printf("%s → %s error: %s", conn.RemoteAddr(), conn.LocalAddr(), err) - } - break - } - if DEBUG_NET { - log.Printf("%s → %s %s", conn.RemoteAddr(), conn.LocalAddr(), line) - } - - ch <- strings.TrimSpace(line) - } - }() - return ch -} - -func maybeLogWriteError(conn net.Conn, err error) bool { - if err != nil { - if err != io.EOF { - log.Printf("%s ← %s error: %s", conn.RemoteAddr(), conn.LocalAddr(), err) - } - return true - } - return false -} - -func StringWriteChan(conn net.Conn) chan<- string { - ch := make(chan string) - writer := bufio.NewWriter(conn) - go func() { - for str := range ch { - if DEBUG_NET { - log.Printf("%s ← %s %s", conn.RemoteAddr(), conn.LocalAddr(), str) - } - if _, err := writer.WriteString(str); maybeLogWriteError(conn, err) { - break - } - if _, err := writer.WriteString(CRLF); maybeLogWriteError(conn, err) { - break - } - if err := writer.Flush(); maybeLogWriteError(conn, err) { - break - } - } - }() - return ch -} - func AddrLookupHostname(addr net.Addr) string { addrStr := addr.String() ipaddr, _, err := net.SplitHostPort(addrStr) diff --git a/irc/reply.go b/irc/reply.go index a1cf54f8..58059a9a 100644 --- a/irc/reply.go +++ b/irc/reply.go @@ -41,8 +41,8 @@ func NewStringReply(source Identifier, code string, } } -func (reply *StringReply) Format(client *Client, write chan<- string) { - write <- reply.message +func (reply *StringReply) Format(client *Client) []string { + return []string{reply.message} } func (reply *StringReply) String() string { @@ -63,8 +63,8 @@ func NewNumericReply(source Identifier, code int, format string, } } -func (reply *NumericReply) Format(client *Client, write chan<- string) { - write <- reply.FormatString(client) +func (reply *NumericReply) Format(client *Client) []string { + return []string{reply.FormatString(client)} } func (reply *NumericReply) FormatString(client *Client) string { @@ -93,7 +93,8 @@ func NewNamesReply(channel *Channel) Reply { } } -func (reply *NamesReply) Format(client *Client, write chan<- string) { +func (reply *NamesReply) Format(client *Client) []string { + lines := make([]string, 0) base := RplNamReply(reply.channel, []string{}) baseLen := len(base.FormatString(client)) tooLong := func(names []string) bool { @@ -103,16 +104,17 @@ func (reply *NamesReply) Format(client *Client, write chan<- string) { nicks := reply.channel.Nicks() for to < len(nicks) { if (from < (to - 1)) && tooLong(nicks[from:to]) { - RplNamReply(reply.channel, nicks[from:to-1]).Format(client, write) + lines = append(lines, RplNamReply(reply.channel, nicks[from:to-1]).Format(client)...) from, to = to-1, to } else { to += 1 } } if from < len(nicks) { - RplNamReply(reply.channel, nicks[from:]).Format(client, write) + lines = append(lines, RplNamReply(reply.channel, nicks[from:]).Format(client)...) } - RplEndOfNames(reply.channel).Format(client, write) + lines = append(lines, RplEndOfNames(reply.channel).Format(client)...) + return lines } func (reply *NamesReply) String() string { diff --git a/irc/types.go b/irc/types.go index c9681cab..3ecd248d 100644 --- a/irc/types.go +++ b/irc/types.go @@ -112,7 +112,7 @@ type Replier interface { } type Reply interface { - Format(*Client, chan<- string) + Format(*Client) []string Source() Identifier }