From: Matt Joiner <anacrolix@gmail.com>
Date: Wed, 30 Sep 2020 06:56:27 +0000 (+1000)
Subject: Limit half-open connections at the Client level
X-Git-Tag: v1.17.0~8
X-Git-Url: http://www.git.stargrave.org/?a=commitdiff_plain;h=a2c7b384dfb62f7724d28f9d846fb7e72c2bb4b2;p=btrtrc.git

Limit half-open connections at the Client level
---

diff --git a/client.go b/client.go
index fafb6e2d..6292d778 100644
--- a/client.go
+++ b/client.go
@@ -75,6 +75,7 @@ type Client struct {
 
 	acceptLimiter   map[ipStr]int
 	dialRateLimiter *rate.Limiter
+	numHalfOpen     int
 
 	websocketTrackers websocketTrackers
 }
@@ -658,7 +659,10 @@ func (cl *Client) noLongerHalfOpen(t *Torrent, addr string) {
 		panic("invariant broken")
 	}
 	delete(t.halfOpen, addr)
-	t.openNewConns()
+	cl.numHalfOpen--
+	for _, t := range cl.torrents {
+		t.openNewConns()
+	}
 }
 
 // Performs initiator handshakes and returns a connection. Returns nil *connection if no connection
diff --git a/config.go b/config.go
index c28ee124..4af86cf6 100644
--- a/config.go
+++ b/config.go
@@ -102,6 +102,7 @@ type ClientConfig struct {
 	MinDialTimeout             time.Duration
 	EstablishedConnsPerTorrent int
 	HalfOpenConnsPerTorrent    int
+	TotalHalfOpenConns         int
 	// Maximum number of peer addresses in reserve.
 	TorrentPeersHighWater int
 	// Minumum number of peers before effort is made to obtain more peers.
@@ -155,6 +156,7 @@ func NewDefaultClientConfig() *ClientConfig {
 		MinDialTimeout:                 3 * time.Second,
 		EstablishedConnsPerTorrent:     50,
 		HalfOpenConnsPerTorrent:        25,
+		TotalHalfOpenConns:             100,
 		TorrentPeersHighWater:          500,
 		TorrentPeersLowWater:           50,
 		HandshakesTimeout:              4 * time.Second,
diff --git a/torrent.go b/torrent.go
index 639e330e..21069ec5 100644
--- a/torrent.go
+++ b/torrent.go
@@ -1075,7 +1075,7 @@ func (t *Torrent) maxHalfOpen() int {
 	return int(min(max(5, extraIncoming)+establishedHeadroom, int64(t.cl.config.HalfOpenConnsPerTorrent)))
 }
 
-func (t *Torrent) openNewConns() {
+func (t *Torrent) openNewConns() (initiated int) {
 	defer t.updateWantPeersEvent()
 	for t.peers.Len() != 0 {
 		if !t.wantConns() {
@@ -1087,9 +1087,14 @@ func (t *Torrent) openNewConns() {
 		if len(t.cl.dialers) == 0 {
 			return
 		}
+		if t.cl.numHalfOpen >= t.cl.config.TotalHalfOpenConns {
+			return
+		}
 		p := t.peers.PopMax()
 		t.initiateConn(p)
+		initiated++
 	}
+	return
 }
 
 func (t *Torrent) getConnPieceInclination() []int {
@@ -1889,7 +1894,6 @@ func (t *Torrent) initiateConn(peer PeerInfo) {
 	if peer.Id == t.cl.peerID {
 		return
 	}
-
 	if t.cl.badPeerAddr(peer.Addr) && !peer.Trusted {
 		return
 	}
@@ -1897,6 +1901,7 @@ func (t *Torrent) initiateConn(peer PeerInfo) {
 	if t.addrActive(addr.String()) {
 		return
 	}
+	t.cl.numHalfOpen++
 	t.halfOpen[addr.String()] = peer
 	go t.cl.outgoingConnection(t, addr, peer.Source, peer.Trusted)
 }