split out connection info and throttling into ircConn; move conn from state to client

This commit is contained in:
Liam Stanley 2017-02-12 02:22:41 -05:00
parent 1090fd92a8
commit ed6e266cba
3 changed files with 55 additions and 61 deletions

View File

@ -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

40
conn.go
View File

@ -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

View File

@ -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
}