diff --git a/irc/client.go b/irc/client.go index 31abd3b9..b56a1768 100644 --- a/irc/client.go +++ b/irc/client.go @@ -75,6 +75,7 @@ type Client struct { func NewClient(server *Server, conn net.Conn, isTLS bool) *Client { now := time.Now() socket := NewSocket(conn) + go socket.RunSocketWriter() client := &Client{ atime: now, authorized: server.password == nil, diff --git a/irc/socket.go b/irc/socket.go index 2764151c..3c76649e 100644 --- a/irc/socket.go +++ b/irc/socket.go @@ -10,9 +10,11 @@ import ( "crypto/tls" "encoding/hex" "errors" + "fmt" "io" "net" "strings" + "sync" "time" ) @@ -27,23 +29,25 @@ type Socket struct { Closed bool conn net.Conn reader *bufio.Reader + + lineToSendExists chan bool + linesToSend []string + linesToSendMutex sync.Mutex } // NewSocket returns a new Socket. func NewSocket(conn net.Conn) Socket { return Socket{ - conn: conn, - reader: bufio.NewReader(conn), + conn: conn, + reader: bufio.NewReader(conn), + lineToSendExists: make(chan bool), } } // Close stops a Socket from being able to send/receive any more data. func (socket *Socket) Close() { - if socket.Closed { - return - } socket.Closed = true - socket.conn.Close() + // socket will close once all data has been sent } // CertFP returns the fingerprint of the certificate provided by the client. @@ -104,15 +108,57 @@ func (socket *Socket) Write(data string) error { return io.EOF } - // write data - _, err := socket.conn.Write([]byte(data)) - if err != nil { - socket.Close() - return err - } + socket.linesToSendMutex.Lock() + socket.linesToSend = append(socket.linesToSend, data) + socket.linesToSendMutex.Unlock() + go socket.fillLineToSendExists() + return nil } +// fillLineToSendExists only exists because you can't goroutine single statements. +func (socket *Socket) fillLineToSendExists() { + socket.lineToSendExists <- true +} + +// RunSocketWriter starts writing messages to the outgoing socket. +func (socket *Socket) RunSocketWriter() { + var errOut bool + for { + // wait for new lines + select { + case <-socket.lineToSendExists: + socket.linesToSendMutex.Lock() + + // get data + data := socket.linesToSend[0] + if len(socket.linesToSend) > 1 { + socket.linesToSend = socket.linesToSend[1:] + } else { + socket.linesToSend = []string{} + } + + // write data + _, err := socket.conn.Write([]byte(data)) + if err != nil { + errOut = true + fmt.Println(err.Error()) + break + } + socket.linesToSendMutex.Unlock() + } + if errOut { + // error out, bad stuff happened + break + } + } + //TODO(dan): empty socket.lineToSendExists queue + socket.conn.Close() + if !socket.Closed { + socket.Closed = true + } +} + // WriteLine writes the given line out of Socket. func (socket *Socket) WriteLine(line string) error { return socket.Write(line + "\r\n")