]> Sergey Matveev's repositories - btrtrc.git/blob - tracker_scraper.go
Improve readability of tracker status line
[btrtrc.git] / tracker_scraper.go
1 package torrent
2
3 import (
4         "bytes"
5         "errors"
6         "fmt"
7         "time"
8
9         "github.com/anacrolix/missinggo"
10
11         "github.com/anacrolix/torrent/tracker"
12 )
13
14 // Announces a torrent to a tracker at regular intervals, when peers are
15 // required.
16 type trackerScraper struct {
17         url string
18         // Causes the trackerScraper to stop running.
19         stop         missinggo.Event
20         t            *Torrent
21         lastAnnounce trackerAnnounceResult
22 }
23
24 func (ts *trackerScraper) statusLine() string {
25         var w bytes.Buffer
26         fmt.Fprintf(&w, "%q\t%s\t%s",
27                 ts.url,
28                 func() string {
29                         // return ts.lastAnnounce.Completed.Add(ts.lastAnnounce.Interval).Format("2006-01-02 15:04:05 -0700 MST")
30                         na := ts.lastAnnounce.Completed.Add(ts.lastAnnounce.Interval).Sub(time.Now())
31                         na /= time.Second
32                         na *= time.Second
33                         if na > 0 {
34                                 return na.String()
35                         } else {
36                                 return "anytime"
37                         }
38                 }(),
39                 func() string {
40                         if ts.lastAnnounce.Err != nil {
41                                 return ts.lastAnnounce.Err.Error()
42                         }
43                         if ts.lastAnnounce.Completed.IsZero() {
44                                 return "never"
45                         }
46                         return fmt.Sprintf("%d peers", ts.lastAnnounce.NumPeers)
47                 }())
48         return w.String()
49 }
50
51 type trackerAnnounceResult struct {
52         Err       error
53         NumPeers  int
54         Interval  time.Duration
55         Completed time.Time
56 }
57
58 func trackerToTorrentPeers(ps []tracker.Peer) (ret []Peer) {
59         ret = make([]Peer, 0, len(ps))
60         for _, p := range ps {
61                 ret = append(ret, Peer{
62                         IP:     p.IP,
63                         Port:   p.Port,
64                         Source: peerSourceTracker,
65                 })
66         }
67         return
68 }
69
70 // Return how long to wait before trying again. For most errors, we return 5
71 // minutes, a relatively quick turn around for DNS changes.
72 func (me *trackerScraper) announce() (ret trackerAnnounceResult) {
73         defer func() {
74                 ret.Completed = time.Now()
75         }()
76         ret.Interval = 5 * time.Minute
77         blocked, urlToUse, host, err := me.t.cl.prepareTrackerAnnounceUnlocked(me.url)
78         if err != nil {
79                 ret.Err = err
80                 return
81         }
82         if blocked {
83                 ret.Err = errors.New("blocked by IP")
84                 return
85         }
86         me.t.cl.mu.Lock()
87         req := me.t.announceRequest()
88         me.t.cl.mu.Unlock()
89         res, err := tracker.AnnounceHost(urlToUse, &req, host)
90         if err != nil {
91                 ret.Err = err
92                 return
93         }
94         me.t.AddPeers(trackerToTorrentPeers(res.Peers))
95         ret.NumPeers = len(res.Peers)
96         ret.Interval = time.Duration(res.Interval) * time.Second
97         return
98 }
99
100 func (me *trackerScraper) Run() {
101         for {
102                 select {
103                 case <-me.t.closed.LockedChan(&me.t.cl.mu):
104                         return
105                 case <-me.stop.LockedChan(&me.t.cl.mu):
106                         return
107                 case <-me.t.wantPeersEvent.LockedChan(&me.t.cl.mu):
108                 }
109
110                 ar := me.announce()
111                 me.t.cl.mu.Lock()
112                 me.lastAnnounce = ar
113                 me.t.cl.mu.Unlock()
114
115                 intervalChan := time.After(ar.Completed.Add(ar.Interval).Sub(time.Now()))
116
117                 select {
118                 case <-me.t.closed.LockedChan(&me.t.cl.mu):
119                         return
120                 case <-me.stop.LockedChan(&me.t.cl.mu):
121                         return
122                 case <-intervalChan:
123                 }
124         }
125 }