github.com/MetalBlockchain/metalgo@v1.11.9/network/throttling/inbound_conn_throttler.go (about)

     1  // Copyright (C) 2019-2024, Ava Labs, Inc. All rights reserved.
     2  // See the file LICENSE for licensing terms.
     3  
     4  package throttling
     5  
     6  import (
     7  	"context"
     8  	"net"
     9  
    10  	"golang.org/x/time/rate"
    11  )
    12  
    13  var _ net.Listener = (*throttledListener)(nil)
    14  
    15  // Wraps [listener] and returns a net.Listener that will accept at most
    16  // [maxConnsPerSec] connections per second.
    17  // [maxConnsPerSec] must be non-negative.
    18  func NewThrottledListener(listener net.Listener, maxConnsPerSec float64) net.Listener {
    19  	ctx, cancel := context.WithCancel(context.Background())
    20  	return &throttledListener{
    21  		ctx:           ctx,
    22  		ctxCancelFunc: cancel,
    23  		listener:      listener,
    24  		limiter:       rate.NewLimiter(rate.Limit(maxConnsPerSec), int(maxConnsPerSec)+1),
    25  	}
    26  }
    27  
    28  // [throttledListener] is a net.Listener that rate-limits
    29  // acceptance of incoming connections.
    30  // Note that InboundConnUpgradeThrottler rate-limits _upgrading_ of
    31  // inbound connections, whereas throttledListener rate-limits
    32  // _acceptance_ of inbound connections.
    33  type throttledListener struct {
    34  	// [ctx] is cancelled when Close() is called
    35  	ctx context.Context
    36  	// [ctxCancelFunc] cancels [ctx] when it's called
    37  	ctxCancelFunc func()
    38  	// The underlying listener
    39  	listener net.Listener
    40  	// Handles rate-limiting
    41  	limiter *rate.Limiter
    42  }
    43  
    44  func (l *throttledListener) Accept() (net.Conn, error) {
    45  	// Wait until the rate-limiter says to accept the
    46  	// next incoming connection. If l.Close() is called,
    47  	// Wait will return immediately.
    48  	if err := l.limiter.Wait(l.ctx); err != nil {
    49  		return nil, err
    50  	}
    51  	return l.listener.Accept()
    52  }
    53  
    54  func (l *throttledListener) Close() error {
    55  	// Cancel [l.ctx] so Accept() will return immediately
    56  	l.ctxCancelFunc()
    57  	return l.listener.Close()
    58  }
    59  
    60  func (l *throttledListener) Addr() net.Addr {
    61  	return l.listener.Addr()
    62  }