From ed6e266cba1dd481f06d200eeb485749c62d4e19 Mon Sep 17 00:00:00 2001 From: Liam Stanley Date: Sun, 12 Feb 2017 02:22:41 -0500 Subject: [PATCH] split out connection info and throttling into ircConn; move conn from state to client --- client.go | 45 ++++++++++++++++----------------------------- conn.go | 40 +++++++++++++++++++++++++++++++++++++++- state.go | 31 ------------------------------- 3 files changed, 55 insertions(+), 61 deletions(-) diff --git a/client.go b/client.go index 190e54b..8623d66 100644 --- a/client.go +++ b/client.go @@ -35,6 +35,8 @@ type Client struct { // CTCP is a handler which manages internal and external CTCP handlers. CTCP *CTCP + // conn is a net.Conn reference to the IRC server. + conn *ircConn // tries represents the internal reconnect count to the IRC server. tries int // reconnecting is true if the client is reconnecting, used so multiple @@ -203,9 +205,7 @@ func (c *Client) Connect() error { return err } - c.state.mu.Lock() - c.state.conn = conn - c.state.mu.Unlock() + c.conn = conn // Send a virtual event allowing hooks for successful socket connection. c.Events <- &Event{Command: INITIALIZED, Trailing: c.Server()} @@ -226,12 +226,6 @@ func (c *Client) Connect() error { c.tries = 0 c.reconnecting = false - c.state.mu.Lock() - ctime := time.Now() - c.state.connTime = &ctime - c.state.connected = true - c.state.mu.Unlock() - // Start read loop to process messages from the server. var rctx, ectx context.Context rctx, c.closeRead = context.WithCancel(context.Background()) @@ -313,12 +307,10 @@ func (c *Client) Reconnect() error { func (c *Client) cleanup(all bool) { c.cmux.Lock() - c.state.mu.Lock() // Close any connections they have open. - if c.state.conn != nil { - c.state.conn.Close() + if c.conn != nil { + c.conn.Close() } - c.state.mu.Unlock() if c.closeRead != nil { c.closeRead() @@ -380,8 +372,8 @@ func (c *Client) readLoop(ctx context.Context) { case <-ctx.Done(): return default: - c.state.conn.lconn.SetDeadline(time.Now().Add(300 * time.Second)) - event, err = c.state.conn.Decode() + c.conn.setTimeout(300 * time.Second) + event, err = c.conn.Decode() if err != nil { // Attempt a reconnect (if applicable). If it fails, send // the error to c.Config.HandleError to be dealt with, if @@ -483,7 +475,7 @@ func (c *Client) Lifetime() time.Duration { // simply looking to trigger handlers with an event. func (c *Client) Send(event *Event) error { if !c.Config.AllowFlood { - <-time.After(c.state.rate(event.Len())) + <-time.After(c.conn.rate(event.Len())) } return c.write(event) @@ -491,14 +483,14 @@ func (c *Client) Send(event *Event) error { // write is the lower level function to write an event. func (c *Client) write(event *Event) error { - c.state.lastWrite = time.Now() + c.conn.lastWrite = time.Now() // log the event if !event.Sensitive { c.debug.Print("> ", StripRaw(event.String())) } - return c.state.conn.Encode(event) + return c.conn.Encode(event) } // Uptime is the time at which the client successfully connected to the @@ -508,9 +500,7 @@ func (c *Client) Uptime() (up *time.Time, err error) { return nil, ErrNotConnected } - c.state.mu.RLock() - up = c.state.connTime - c.state.mu.RUnlock() + up = c.conn.connTime return up, nil } @@ -522,20 +512,17 @@ func (c *Client) ConnSince() (since *time.Duration, err error) { return nil, ErrNotConnected } - c.state.mu.RLock() - timeSince := time.Since(*c.state.connTime) - c.state.mu.RUnlock() + timeSince := time.Since(*c.conn.connTime) return &timeSince, nil } // IsConnected returns true if the client is connected to the server. func (c *Client) IsConnected() (connected bool) { - c.state.mu.RLock() - connected = c.state.connected - c.state.mu.RUnlock() - - return connected + if c.conn == nil { + return false + } + return c.conn.connected } // GetNick returns the current nickname of the active connection. Returns diff --git a/conn.go b/conn.go index cdb34de..ec8c0e7 100644 --- a/conn.go +++ b/conn.go @@ -29,10 +29,22 @@ var endline = []byte("\r\n") type ircConn struct { ircEncoder ircDecoder - lconn net.Conn + + // lastWrite is used ot keep track of when we last wrote to the server. + lastWrite time.Time + // writeDelay is used to keep track of rate limiting of events sent to + // the server. + writeDelay time.Duration + + // connected is true if we're actively connected to a server. + connected bool + // connTime is the time at which the client has connected to a server. + connTime *time.Time } +// newConn sets up and returns a new connection to the server. This includes +// setting up things like proxies, ssl/tls, and other misc. things. func newConn(conf Config, addr string) (*ircConn, error) { // Sanity check a few options. if conf.Server == "" { @@ -103,10 +115,14 @@ func newConn(conf Config, addr string) (*ircConn, error) { conn = tlsConn } + ctime := time.Now() + return &ircConn{ ircEncoder: ircEncoder{writer: conn}, ircDecoder: ircDecoder{reader: bufio.NewReader(conn)}, lconn: conn, + connTime: &ctime, + connected: true, }, nil } @@ -115,6 +131,28 @@ func (c *ircConn) Close() error { return c.lconn.Close() } +// setTimeout applies a deadline that the connection must respond back with, +// within the specified time. +func (c *ircConn) setTimeout(timeout time.Duration) { + c.lconn.SetDeadline(time.Now().Add(timeout)) +} + +// rate allows limiting events based on how frequent the event is being sent, +// as well as how many characters each event has. +func (c *ircConn) rate(chars int) time.Duration { + _time := time.Second + ((time.Duration(chars) * time.Second) / 100) + elapsed := time.Now().Sub(c.lastWrite) + if c.writeDelay += _time - elapsed; c.writeDelay < 0 { + c.writeDelay = 0 + } + + if c.writeDelay > (8 * time.Second) { + return _time + } + + return 0 +} + // ircDecoder reads Event objects from an input stream. type ircDecoder struct { reader *bufio.Reader diff --git a/state.go b/state.go index f167a88..550b6e8 100644 --- a/state.go +++ b/state.go @@ -17,20 +17,6 @@ type state struct { // m is a RW mutex lock, used to guard the state from goroutines causing // corruption. mu sync.RWMutex - - // conn is a net.Conn reference to the IRC server. - conn *ircConn - - // lastWrite is used ot keep track of when we last wrote to the server. - lastWrite time.Time - // writeDelay is used to keep track of rate limiting of events sent to - // the server. - writeDelay time.Duration - - // connected is true if we're actively connected to a server. - connected bool - // connTime is the time at which the client has connected to a server. - connTime *time.Time // nick is the tracker for our nickname on the server. nick string // channels represents all channels we're active in. @@ -180,7 +166,6 @@ func newState() *state { s.channels = make(map[string]*Channel) s.serverOptions = make(map[string]string) - s.connected = false return s } @@ -338,19 +323,3 @@ func (s *state) lookupUsers(matchType, toMatch string) []*User { return users } - -// rate allows limiting events based on how frequent the event is being sent, -// as well as how many characters each event has. -func (s *state) rate(chars int) time.Duration { - _time := time.Second + ((time.Duration(chars) * time.Second) / 100) - elapsed := time.Now().Sub(s.lastWrite) - if s.writeDelay += _time - elapsed; s.writeDelay < 0 { - s.writeDelay = 0 - } - - if s.writeDelay > (8 * time.Second) { - return _time - } - - return 0 -}