]> Sergey Matveev's repositories - btrtrc.git/commitdiff
Support scraping from HTTP trackers
authorMatt Joiner <anacrolix@gmail.com>
Tue, 26 Sep 2023 12:19:51 +0000 (22:19 +1000)
committerMatt Joiner <anacrolix@gmail.com>
Tue, 26 Sep 2023 12:19:51 +0000 (22:19 +1000)
cmd/torrent/scrape.go
tracker/client.go
tracker/http/scrape.go [new file with mode: 0644]
tracker/udp.go
tracker/udp/scrape.go

index ce5fc06c89e70b8cb04e419cd49b7b3e108b7838..01a59b7ff57ef6dabfb093391335cdc34c8b38ea 100644 (file)
@@ -3,12 +3,11 @@ package main
 import (
        "context"
        "fmt"
-       "net/url"
 
        "github.com/davecgh/go-spew/spew"
 
        "github.com/anacrolix/torrent"
-       "github.com/anacrolix/torrent/tracker/udp"
+       "github.com/anacrolix/torrent/tracker"
 )
 
 type scrapeCfg struct {
@@ -17,25 +16,13 @@ type scrapeCfg struct {
 }
 
 func scrape(flags scrapeCfg) error {
-       trackerUrl, err := url.Parse(flags.Tracker)
+       cc, err := tracker.NewClient(flags.Tracker, tracker.NewClientOpts{})
        if err != nil {
-               return fmt.Errorf("parsing tracker url: %w", err)
-       }
-       cc, err := udp.NewConnClient(udp.NewConnClientOpts{
-               Network: trackerUrl.Scheme,
-               Host:    trackerUrl.Host,
-               //Ipv6:    nil,
-               //Logger:  log.Logger{},
-       })
-       if err != nil {
-               return fmt.Errorf("creating new udp tracker conn client: %w", err)
+               err = fmt.Errorf("creating new tracker client: %w", err)
+               return err
        }
        defer cc.Close()
-       var ihs []udp.InfoHash
-       for _, ih := range flags.InfoHashes {
-               ihs = append(ihs, ih)
-       }
-       scrapeOut, err := cc.Client.Scrape(context.TODO(), ihs)
+       scrapeOut, err := cc.Scrape(context.TODO(), flags.InfoHashes)
        if err != nil {
                return fmt.Errorf("scraping: %w", err)
        }
index 1aaf25611964cb26f5b2c032ea106af86d29c33c..3b7e2aba36b54b6f09c75a43791bf671811a3f3a 100644 (file)
@@ -9,10 +9,12 @@ import (
 
        trHttp "github.com/anacrolix/torrent/tracker/http"
        "github.com/anacrolix/torrent/tracker/udp"
+       "github.com/anacrolix/torrent/types/infohash"
 )
 
 type Client interface {
        Announce(context.Context, AnnounceRequest, AnnounceOpt) (AnnounceResponse, error)
+       Scrape(ctx context.Context, ihs []infohash.T) (out udp.ScrapeResponse, err error)
        Close() error
 }
 
diff --git a/tracker/http/scrape.go b/tracker/http/scrape.go
new file mode 100644 (file)
index 0000000..6940370
--- /dev/null
@@ -0,0 +1,47 @@
+package httpTracker
+
+import (
+       "context"
+       "log"
+       "net/http"
+       "net/url"
+
+       "github.com/anacrolix/torrent/bencode"
+       "github.com/anacrolix/torrent/tracker/udp"
+       "github.com/anacrolix/torrent/types/infohash"
+)
+
+type scrapeResponse struct {
+       Files files `bencode:"files"`
+}
+
+// Bencode should support bencode.Unmarshalers from a string in the dict key position.
+type files = map[string]udp.ScrapeInfohashResult
+
+func (cl Client) Scrape(ctx context.Context, ihs []infohash.T) (out udp.ScrapeResponse, err error) {
+       _url := cl.url_.JoinPath("..", "scrape")
+       query, err := url.ParseQuery(_url.RawQuery)
+       if err != nil {
+               return
+       }
+       for _, ih := range ihs {
+               query.Add("info_hash", ih.AsString())
+       }
+       _url.RawQuery = query.Encode()
+       log.Printf("%q", _url.String())
+       req, err := http.NewRequestWithContext(ctx, http.MethodGet, _url.String(), nil)
+       if err != nil {
+               return
+       }
+       resp, err := cl.hc.Do(req)
+       if err != nil {
+               return
+       }
+       defer resp.Body.Close()
+       var decodedResp scrapeResponse
+       err = bencode.NewDecoder(resp.Body).Decode(&decodedResp)
+       for _, ih := range ihs {
+               out = append(out, decodedResp.Files[ih.AsString()])
+       }
+       return
+}
index db486948a675447208c388cf18bbb488495fece2..cf68188751d6c0bda395936a176f6cc38e17ebc9 100644 (file)
@@ -4,8 +4,11 @@ import (
        "context"
        "encoding/binary"
 
+       "github.com/anacrolix/generics"
+
        trHttp "github.com/anacrolix/torrent/tracker/http"
        "github.com/anacrolix/torrent/tracker/udp"
+       "github.com/anacrolix/torrent/types/infohash"
 )
 
 type udpClient struct {
@@ -13,6 +16,15 @@ type udpClient struct {
        requestUri string
 }
 
+func (c *udpClient) Scrape(ctx context.Context, ihs []infohash.T) (out udp.ScrapeResponse, err error) {
+       return c.cl.Client.Scrape(
+               ctx,
+               generics.SliceMap(ihs, func(from infohash.T) udp.InfoHash {
+                       return from
+               }),
+       )
+}
+
 func (c *udpClient) Close() error {
        return c.cl.Close()
 }
index 331f109e629d0c05d5c80481e5fca5b0657ae4e0..13a69b9919d9ef746910548ca3d27156a4cb2f9e 100644 (file)
@@ -5,7 +5,9 @@ type ScrapeRequest []InfoHash
 type ScrapeResponse []ScrapeInfohashResult
 
 type ScrapeInfohashResult struct {
-       Seeders   int32
-       Completed int32
-       Leechers  int32
+       // I'm not sure why the fields are named differently for HTTP scrapes.
+       // https://www.bittorrent.org/beps/bep_0048.html
+       Seeders   int32 `bencode:"complete"`
+       Completed int32 `bencode:"downloaded"`
+       Leechers  int32 `bencode:"incomplete"`
 }