better checks before ratelimiting; drop events if disconnected (closes #14)
This commit is contained in:
parent
f98113f16d
commit
8adc1b3054
22
client.go
22
client.go
@ -754,3 +754,25 @@ func (c *Client) panicIfNotTracking() {
|
|||||||
|
|
||||||
panic(fmt.Sprintf("%s used when tracking is disabled (caller %s:%d)", fn.Name(), file, line))
|
panic(fmt.Sprintf("%s used when tracking is disabled (caller %s:%d)", fn.Name(), file, line))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (c *Client) debugLogEvent(e *Event, dropped bool) {
|
||||||
|
var prefix string
|
||||||
|
|
||||||
|
if dropped {
|
||||||
|
prefix = "dropping event (disconnected):"
|
||||||
|
} else {
|
||||||
|
prefix = ">"
|
||||||
|
}
|
||||||
|
|
||||||
|
if e.Sensitive {
|
||||||
|
c.debug.Printf(prefix, " %s ***redacted***", e.Command)
|
||||||
|
} else {
|
||||||
|
c.debug.Print(prefix, " ", StripRaw(e.String()))
|
||||||
|
}
|
||||||
|
|
||||||
|
if c.Config.Out != nil {
|
||||||
|
if pretty, ok := e.Pretty(); ok {
|
||||||
|
fmt.Fprintln(c.Config.Out, StripRaw(pretty))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
44
conn.go
44
conn.go
@ -430,8 +430,23 @@ func (c *Client) readLoop(ctx context.Context, errs chan error, wg *sync.WaitGro
|
|||||||
// Send sends an event to the server. Use Client.RunHandlers() if you are
|
// Send sends an event to the server. Use Client.RunHandlers() if you are
|
||||||
// simply looking to trigger handlers with an event.
|
// simply looking to trigger handlers with an event.
|
||||||
func (c *Client) Send(event *Event) {
|
func (c *Client) Send(event *Event) {
|
||||||
|
var delay time.Duration
|
||||||
|
|
||||||
if !c.Config.AllowFlood {
|
if !c.Config.AllowFlood {
|
||||||
<-time.After(c.conn.rate(event.Len()))
|
c.mu.RLock()
|
||||||
|
|
||||||
|
// Drop the event early as we're disconnected, this way we don't have to wait
|
||||||
|
// the (potentially long) rate limit delay before dropping.
|
||||||
|
if c.conn == nil {
|
||||||
|
c.debugLogEvent(event, true)
|
||||||
|
c.mu.RUnlock()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
c.conn.mu.Lock()
|
||||||
|
delay = c.conn.rate(event.Len())
|
||||||
|
c.conn.mu.Unlock()
|
||||||
|
c.mu.RUnlock()
|
||||||
}
|
}
|
||||||
|
|
||||||
if c.Config.GlobalFormat && len(event.Params) > 0 && event.Params[len(event.Params)-1] != "" &&
|
if c.Config.GlobalFormat && len(event.Params) > 0 && event.Params[len(event.Params)-1] != "" &&
|
||||||
@ -439,7 +454,18 @@ func (c *Client) Send(event *Event) {
|
|||||||
event.Params[len(event.Params)-1] = Fmt(event.Params[len(event.Params)-1])
|
event.Params[len(event.Params)-1] = Fmt(event.Params[len(event.Params)-1])
|
||||||
}
|
}
|
||||||
|
|
||||||
|
<-time.After(delay)
|
||||||
|
|
||||||
|
// Relock client again as there may be an extended delay.
|
||||||
|
c.mu.RLock()
|
||||||
|
if c.conn == nil {
|
||||||
|
// Drop the event if disconnected.
|
||||||
|
c.debugLogEvent(event, true)
|
||||||
|
c.mu.RUnlock()
|
||||||
|
return
|
||||||
|
}
|
||||||
c.write(event)
|
c.write(event)
|
||||||
|
c.mu.RUnlock()
|
||||||
}
|
}
|
||||||
|
|
||||||
// write is the lower level function to write an event. It does not have a
|
// write is the lower level function to write an event. It does not have a
|
||||||
@ -453,14 +479,10 @@ func (c *Client) write(event *Event) {
|
|||||||
func (c *ircConn) rate(chars int) time.Duration {
|
func (c *ircConn) rate(chars int) time.Duration {
|
||||||
_time := time.Second + ((time.Duration(chars) * time.Second) / 100)
|
_time := time.Second + ((time.Duration(chars) * time.Second) / 100)
|
||||||
|
|
||||||
c.mu.Lock()
|
|
||||||
if c.writeDelay += _time - time.Now().Sub(c.lastWrite); c.writeDelay < 0 {
|
if c.writeDelay += _time - time.Now().Sub(c.lastWrite); c.writeDelay < 0 {
|
||||||
c.writeDelay = 0
|
c.writeDelay = 0
|
||||||
}
|
}
|
||||||
c.mu.Unlock()
|
|
||||||
|
|
||||||
c.mu.RLock()
|
|
||||||
defer c.mu.RUnlock()
|
|
||||||
if c.writeDelay > (8 * time.Second) {
|
if c.writeDelay > (8 * time.Second) {
|
||||||
return _time
|
return _time
|
||||||
}
|
}
|
||||||
@ -495,17 +517,7 @@ func (c *Client) sendLoop(ctx context.Context, errs chan error, wg *sync.WaitGro
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Log the event.
|
c.debugLogEvent(event, false)
|
||||||
if event.Sensitive {
|
|
||||||
c.debug.Printf("> %s ***redacted***", event.Command)
|
|
||||||
} else {
|
|
||||||
c.debug.Print("> ", StripRaw(event.String()))
|
|
||||||
}
|
|
||||||
if c.Config.Out != nil {
|
|
||||||
if pretty, ok := event.Pretty(); ok {
|
|
||||||
fmt.Fprintln(c.Config.Out, StripRaw(pretty))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
c.conn.mu.Lock()
|
c.conn.mu.Lock()
|
||||||
c.conn.lastWrite = time.Now()
|
c.conn.lastWrite = time.Now()
|
||||||
|
Loading…
Reference in New Issue
Block a user