From 9869f8f5a9220d71d56e9dce7dc1c9ddc8dbb884 Mon Sep 17 00:00:00 2001 From: Matt Joiner Date: Thu, 26 Mar 2015 17:20:31 +1100 Subject: [PATCH] Almost completed http tracker support --- tracker/http/httptracker.go | 112 ++++++++++++++++++++++++++++++++++++ tracker/tracker.go | 5 ++ 2 files changed, 117 insertions(+) create mode 100644 tracker/http/httptracker.go diff --git a/tracker/http/httptracker.go b/tracker/http/httptracker.go new file mode 100644 index 00000000..432d00cf --- /dev/null +++ b/tracker/http/httptracker.go @@ -0,0 +1,112 @@ +package http + +import ( + "bytes" + "errors" + "fmt" + "io" + "log" + "net" + "net/http" + "net/url" + "strconv" + + "github.com/anacrolix/torrent/util" + + "github.com/anacrolix/libtorgo/bencode" + + "github.com/anacrolix/torrent/tracker" +) + +func init() { + tracker.RegisterClientScheme("http", NewClient) +} + +type client struct { + url url.URL +} + +func NewClient(url *url.URL) tracker.Client { + return &client{ + url: *url, + } +} + +type response struct { + FailureReason string `bencode:"failure reason"` + Interval int32 `bencode:"interval"` + TrackerId string `bencode:"tracker id"` + Complete int32 `bencode:"complete"` + Incomplete int32 `bencode:"incomplete"` + Peers interface{} `bencode:"peers"` +} + +func (r *response) UnmarshalPeers() (ret []tracker.Peer, err error) { + s, ok := r.Peers.(string) + if !ok { + err = fmt.Errorf("unsupported peers value type: %T", r.Peers) + return + } + cp := make(util.CompactPeers, 0, len(s)/6) + err = cp.UnmarshalBinary([]byte(s)) + if err != nil { + return + } + ret = make([]tracker.Peer, 0, len(cp)) + for _, p := range cp { + ret = append(ret, tracker.Peer{net.IP(p.IP[:]), int(p.Port)}) + } + return +} + +func (me *client) Announce(ar *tracker.AnnounceRequest) (ret tracker.AnnounceResponse, err error) { + q := make(url.Values) + q.Set("info_hash", string(ar.InfoHash[:])) + q.Set("peer_id", string(ar.PeerId[:])) + q.Set("port", fmt.Sprintf("%d", ar.Port)) + q.Set("uploaded", strconv.FormatInt(ar.Uploaded, 10)) + q.Set("downloaded", strconv.FormatInt(ar.Downloaded, 10)) + q.Set("left", strconv.FormatInt(ar.Left, 10)) + if ar.Event != tracker.None { + q.Set("event", ar.Event.String()) + } + // http://stackoverflow.com/questions/17418004/why-does-tracker-server-not-understand-my-request-bittorrent-protocol + q.Set("compact", "1") + var reqURL url.URL = me.url + reqURL.RawQuery = q.Encode() + resp, err := http.Get(reqURL.String()) + if err != nil { + return + } + defer resp.Body.Close() + buf := bytes.Buffer{} + io.Copy(&buf, resp.Body) + log.Printf("%q", buf.Bytes()) + var trackerResponse response + err = bencode.NewDecoder(&buf).Decode(&trackerResponse) + if err != nil { + return + } + if trackerResponse.FailureReason != "" { + err = errors.New(trackerResponse.FailureReason) + return + } + ret.Interval = trackerResponse.Interval + ret.Leechers = trackerResponse.Incomplete + ret.Seeders = trackerResponse.Complete + ret.Peers, err = trackerResponse.UnmarshalPeers() + return +} + +func (me *client) Connect() error { + // HTTP trackers do not require a connecting handshake. + return nil +} + +func (me *client) String() string { + return me.URL() +} + +func (me *client) URL() string { + return me.url.String() +} diff --git a/tracker/tracker.go b/tracker/tracker.go index 888228ec..98a77a18 100644 --- a/tracker/tracker.go +++ b/tracker/tracker.go @@ -28,6 +28,11 @@ type AnnounceResponse struct { type AnnounceEvent int32 +func (me AnnounceEvent) String() string { + // See BEP 3, "event". + return []string{"empty", "completed", "started", "stopped"}[me] +} + type Peer struct { IP net.IP Port int -- 2.48.1