split out connection info and throttling into ircConn; move conn from state to client
This commit is contained in:
parent
1090fd92a8
commit
ed6e266cba
45
client.go
45
client.go
@ -35,6 +35,8 @@ type Client struct {
|
|||||||
// CTCP is a handler which manages internal and external CTCP handlers.
|
// CTCP is a handler which manages internal and external CTCP handlers.
|
||||||
CTCP *CTCP
|
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 represents the internal reconnect count to the IRC server.
|
||||||
tries int
|
tries int
|
||||||
// reconnecting is true if the client is reconnecting, used so multiple
|
// reconnecting is true if the client is reconnecting, used so multiple
|
||||||
@ -203,9 +205,7 @@ func (c *Client) Connect() error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
c.state.mu.Lock()
|
c.conn = conn
|
||||||
c.state.conn = conn
|
|
||||||
c.state.mu.Unlock()
|
|
||||||
|
|
||||||
// Send a virtual event allowing hooks for successful socket connection.
|
// Send a virtual event allowing hooks for successful socket connection.
|
||||||
c.Events <- &Event{Command: INITIALIZED, Trailing: c.Server()}
|
c.Events <- &Event{Command: INITIALIZED, Trailing: c.Server()}
|
||||||
@ -226,12 +226,6 @@ func (c *Client) Connect() error {
|
|||||||
c.tries = 0
|
c.tries = 0
|
||||||
c.reconnecting = false
|
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.
|
// Start read loop to process messages from the server.
|
||||||
var rctx, ectx context.Context
|
var rctx, ectx context.Context
|
||||||
rctx, c.closeRead = context.WithCancel(context.Background())
|
rctx, c.closeRead = context.WithCancel(context.Background())
|
||||||
@ -313,12 +307,10 @@ func (c *Client) Reconnect() error {
|
|||||||
func (c *Client) cleanup(all bool) {
|
func (c *Client) cleanup(all bool) {
|
||||||
c.cmux.Lock()
|
c.cmux.Lock()
|
||||||
|
|
||||||
c.state.mu.Lock()
|
|
||||||
// Close any connections they have open.
|
// Close any connections they have open.
|
||||||
if c.state.conn != nil {
|
if c.conn != nil {
|
||||||
c.state.conn.Close()
|
c.conn.Close()
|
||||||
}
|
}
|
||||||
c.state.mu.Unlock()
|
|
||||||
|
|
||||||
if c.closeRead != nil {
|
if c.closeRead != nil {
|
||||||
c.closeRead()
|
c.closeRead()
|
||||||
@ -380,8 +372,8 @@ func (c *Client) readLoop(ctx context.Context) {
|
|||||||
case <-ctx.Done():
|
case <-ctx.Done():
|
||||||
return
|
return
|
||||||
default:
|
default:
|
||||||
c.state.conn.lconn.SetDeadline(time.Now().Add(300 * time.Second))
|
c.conn.setTimeout(300 * time.Second)
|
||||||
event, err = c.state.conn.Decode()
|
event, err = c.conn.Decode()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
// Attempt a reconnect (if applicable). If it fails, send
|
// Attempt a reconnect (if applicable). If it fails, send
|
||||||
// the error to c.Config.HandleError to be dealt with, if
|
// 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.
|
// simply looking to trigger handlers with an event.
|
||||||
func (c *Client) Send(event *Event) error {
|
func (c *Client) Send(event *Event) error {
|
||||||
if !c.Config.AllowFlood {
|
if !c.Config.AllowFlood {
|
||||||
<-time.After(c.state.rate(event.Len()))
|
<-time.After(c.conn.rate(event.Len()))
|
||||||
}
|
}
|
||||||
|
|
||||||
return c.write(event)
|
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.
|
// write is the lower level function to write an event.
|
||||||
func (c *Client) write(event *Event) error {
|
func (c *Client) write(event *Event) error {
|
||||||
c.state.lastWrite = time.Now()
|
c.conn.lastWrite = time.Now()
|
||||||
|
|
||||||
// log the event
|
// log the event
|
||||||
if !event.Sensitive {
|
if !event.Sensitive {
|
||||||
c.debug.Print("> ", StripRaw(event.String()))
|
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
|
// 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
|
return nil, ErrNotConnected
|
||||||
}
|
}
|
||||||
|
|
||||||
c.state.mu.RLock()
|
up = c.conn.connTime
|
||||||
up = c.state.connTime
|
|
||||||
c.state.mu.RUnlock()
|
|
||||||
|
|
||||||
return up, nil
|
return up, nil
|
||||||
}
|
}
|
||||||
@ -522,20 +512,17 @@ func (c *Client) ConnSince() (since *time.Duration, err error) {
|
|||||||
return nil, ErrNotConnected
|
return nil, ErrNotConnected
|
||||||
}
|
}
|
||||||
|
|
||||||
c.state.mu.RLock()
|
timeSince := time.Since(*c.conn.connTime)
|
||||||
timeSince := time.Since(*c.state.connTime)
|
|
||||||
c.state.mu.RUnlock()
|
|
||||||
|
|
||||||
return &timeSince, nil
|
return &timeSince, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// IsConnected returns true if the client is connected to the server.
|
// IsConnected returns true if the client is connected to the server.
|
||||||
func (c *Client) IsConnected() (connected bool) {
|
func (c *Client) IsConnected() (connected bool) {
|
||||||
c.state.mu.RLock()
|
if c.conn == nil {
|
||||||
connected = c.state.connected
|
return false
|
||||||
c.state.mu.RUnlock()
|
}
|
||||||
|
return c.conn.connected
|
||||||
return connected
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetNick returns the current nickname of the active connection. Returns
|
// GetNick returns the current nickname of the active connection. Returns
|
||||||
|
40
conn.go
40
conn.go
@ -29,10 +29,22 @@ var endline = []byte("\r\n")
|
|||||||
type ircConn struct {
|
type ircConn struct {
|
||||||
ircEncoder
|
ircEncoder
|
||||||
ircDecoder
|
ircDecoder
|
||||||
|
|
||||||
lconn net.Conn
|
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) {
|
func newConn(conf Config, addr string) (*ircConn, error) {
|
||||||
// Sanity check a few options.
|
// Sanity check a few options.
|
||||||
if conf.Server == "" {
|
if conf.Server == "" {
|
||||||
@ -103,10 +115,14 @@ func newConn(conf Config, addr string) (*ircConn, error) {
|
|||||||
conn = tlsConn
|
conn = tlsConn
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ctime := time.Now()
|
||||||
|
|
||||||
return &ircConn{
|
return &ircConn{
|
||||||
ircEncoder: ircEncoder{writer: conn},
|
ircEncoder: ircEncoder{writer: conn},
|
||||||
ircDecoder: ircDecoder{reader: bufio.NewReader(conn)},
|
ircDecoder: ircDecoder{reader: bufio.NewReader(conn)},
|
||||||
lconn: conn,
|
lconn: conn,
|
||||||
|
connTime: &ctime,
|
||||||
|
connected: true,
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -115,6 +131,28 @@ func (c *ircConn) Close() error {
|
|||||||
return c.lconn.Close()
|
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.
|
// ircDecoder reads Event objects from an input stream.
|
||||||
type ircDecoder struct {
|
type ircDecoder struct {
|
||||||
reader *bufio.Reader
|
reader *bufio.Reader
|
||||||
|
31
state.go
31
state.go
@ -17,20 +17,6 @@ type state struct {
|
|||||||
// m is a RW mutex lock, used to guard the state from goroutines causing
|
// m is a RW mutex lock, used to guard the state from goroutines causing
|
||||||
// corruption.
|
// corruption.
|
||||||
mu sync.RWMutex
|
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 is the tracker for our nickname on the server.
|
||||||
nick string
|
nick string
|
||||||
// channels represents all channels we're active in.
|
// channels represents all channels we're active in.
|
||||||
@ -180,7 +166,6 @@ func newState() *state {
|
|||||||
|
|
||||||
s.channels = make(map[string]*Channel)
|
s.channels = make(map[string]*Channel)
|
||||||
s.serverOptions = make(map[string]string)
|
s.serverOptions = make(map[string]string)
|
||||||
s.connected = false
|
|
||||||
|
|
||||||
return s
|
return s
|
||||||
}
|
}
|
||||||
@ -338,19 +323,3 @@ func (s *state) lookupUsers(matchType, toMatch string) []*User {
|
|||||||
|
|
||||||
return users
|
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
|
|
||||||
}
|
|
||||||
|
Loading…
Reference in New Issue
Block a user