package p2p
|
|
|
|
import (
|
|
"fmt"
|
|
"net"
|
|
"sync"
|
|
"time"
|
|
)
|
|
|
|
type connectionTracker interface {
|
|
AddConn(net.IP) error
|
|
RemoveConn(net.IP)
|
|
Len() int
|
|
}
|
|
|
|
type connTrackerImpl struct {
|
|
cache map[string]uint
|
|
lastConnect map[string]time.Time
|
|
mutex sync.RWMutex
|
|
max uint
|
|
window time.Duration
|
|
}
|
|
|
|
func newConnTracker(max uint, window time.Duration) connectionTracker {
|
|
return &connTrackerImpl{
|
|
cache: make(map[string]uint),
|
|
lastConnect: make(map[string]time.Time),
|
|
max: max,
|
|
}
|
|
}
|
|
|
|
func (rat *connTrackerImpl) Len() int {
|
|
rat.mutex.RLock()
|
|
defer rat.mutex.RUnlock()
|
|
return len(rat.cache)
|
|
}
|
|
|
|
func (rat *connTrackerImpl) AddConn(addr net.IP) error {
|
|
address := addr.String()
|
|
rat.mutex.Lock()
|
|
defer rat.mutex.Unlock()
|
|
|
|
if num := rat.cache[address]; num >= rat.max {
|
|
return fmt.Errorf("%q has %d connections [max=%d]", address, num, rat.max)
|
|
} else if num == 0 {
|
|
// if there is already at least connection, check to
|
|
// see if it was established before within the window,
|
|
// and error if so.
|
|
if last := rat.lastConnect[address]; time.Since(last) < rat.window {
|
|
return fmt.Errorf("%q tried to connect within window of last %s", address, rat.window)
|
|
}
|
|
}
|
|
|
|
rat.cache[address]++
|
|
rat.lastConnect[address] = time.Now()
|
|
|
|
return nil
|
|
}
|
|
|
|
func (rat *connTrackerImpl) RemoveConn(addr net.IP) {
|
|
address := addr.String()
|
|
rat.mutex.Lock()
|
|
defer rat.mutex.Unlock()
|
|
|
|
if num := rat.cache[address]; num > 0 {
|
|
rat.cache[address]--
|
|
}
|
|
if num := rat.cache[address]; num <= 0 {
|
|
delete(rat.cache, address)
|
|
}
|
|
|
|
if last, ok := rat.lastConnect[address]; ok && time.Since(last) > rat.window {
|
|
delete(rat.lastConnect, address)
|
|
}
|
|
}
|