From: Justin M <37273682+DigitalAlchemist@users.noreply.github.com>
Date: Mon, 4 Jan 2021 23:20:51 +0000 (+0000)
Subject: Reannounce webtorrent webrtc offers on reconnect
X-Git-Tag: v1.21.0~11
X-Git-Url: http://www.git.stargrave.org/?a=commitdiff_plain;h=b2ab540f9381100b26c9a9b3a2652a7e4981f3e0;p=btrtrc.git

Reannounce webtorrent webrtc offers on reconnect

On socket reconnect, reannounce any webrtc offers that might have been half-open before the
socket reconnected. This implementation closes any existing half-open offers and republishes them,
but does nothing to existing webrtc connections which are still running (these are cleaned up
elsewhere and are not owned by this code)
---

diff --git a/webtorrent/tracker_client.go b/webtorrent/tracker_client.go
index 5af6c7c0..47267e0e 100644
--- a/webtorrent/tracker_client.go
+++ b/webtorrent/tracker_client.go
@@ -72,14 +72,14 @@ func (tc *TrackerClient) doWebsocket() error {
 		return fmt.Errorf("dialing tracker: %w", err)
 	}
 	defer c.Close()
-	tc.Logger.WithDefaultLevel(log.Debug).Printf("dialed tracker %q", tc.Url)
+	tc.Logger.WithDefaultLevel(log.Info).Printf("connected")
 	tc.mu.Lock()
 	tc.wsConn = c
 	tc.cond.Broadcast()
 	tc.mu.Unlock()
+	tc.announceOffers()
 	err = tc.trackerReadLoop(tc.wsConn)
 	tc.mu.Lock()
-	tc.closeUnusedOffers()
 	c.Close()
 	tc.mu.Unlock()
 	return err
@@ -111,11 +111,36 @@ func (tc *TrackerClient) Close() error {
 	if tc.wsConn != nil {
 		tc.wsConn.Close()
 	}
+	tc.closeUnusedOffers()
 	tc.mu.Unlock()
 	tc.cond.Broadcast()
 	return nil
 }
 
+func (tc *TrackerClient) announceOffers() {
+
+	// tc.Announce grabs a lock on tc.outboundOffers. It also handles the case where outboundOffers
+	// is nil. Take ownership of outboundOffers here.
+	tc.mu.Lock()
+	offers := tc.outboundOffers
+	tc.outboundOffers = nil
+	tc.mu.Unlock()
+
+	if offers == nil {
+		return
+	}
+
+	// Iterate over our locally-owned offers, close any existing "invalid" ones from before the
+	// socket reconnected, reannounce the infohash, adding it back into the tc.outboundOffers.
+	tc.Logger.WithDefaultLevel(log.Info).Printf("reannouncing %d infohashes after restart", len(offers))
+	for _, offer := range offers {
+		// TODO: Capture the errors? Are we even in a position to do anything with them?
+		offer.peerConnection.Close()
+		// Use goroutine here to allow read loop to start and ensure the buffer drains.
+		go tc.Announce(tracker.Started, offer.infoHash)
+	}
+}
+
 func (tc *TrackerClient) closeUnusedOffers() {
 	for _, offer := range tc.outboundOffers {
 		offer.peerConnection.Close()
@@ -139,6 +164,7 @@ func (tc *TrackerClient) Announce(event tracker.AnnounceEvent, infoHash [20]byte
 
 	request, err := tc.GetAnnounceRequest(event, infoHash)
 	if err != nil {
+		pc.Close()
 		return fmt.Errorf("getting announce parameters: %w", err)
 	}
 
@@ -159,6 +185,7 @@ func (tc *TrackerClient) Announce(event tracker.AnnounceEvent, infoHash [20]byte
 
 	data, err := json.Marshal(req)
 	if err != nil {
+		pc.Close()
 		return fmt.Errorf("marshalling request: %w", err)
 	}