]> Sergey Matveev's repositories - btrtrc.git/blob - tracker/http.go
Apply sortimports
[btrtrc.git] / tracker / http.go
1 package tracker
2
3 import (
4         "bytes"
5         "errors"
6         "fmt"
7         "io"
8         "net"
9         "net/http"
10         "net/url"
11         "strconv"
12
13         "github.com/anacrolix/torrent/bencode"
14         "github.com/anacrolix/torrent/util"
15 )
16
17 func init() {
18         RegisterClientScheme("http", NewClient)
19 }
20
21 type client struct {
22         url url.URL
23 }
24
25 func NewClient(url *url.URL) Client {
26         return &client{
27                 url: *url,
28         }
29 }
30
31 type response struct {
32         FailureReason string      `bencode:"failure reason"`
33         Interval      int32       `bencode:"interval"`
34         TrackerId     string      `bencode:"tracker id"`
35         Complete      int32       `bencode:"complete"`
36         Incomplete    int32       `bencode:"incomplete"`
37         Peers         interface{} `bencode:"peers"`
38 }
39
40 func (r *response) UnmarshalPeers() (ret []Peer, err error) {
41         s, ok := r.Peers.(string)
42         if !ok {
43                 err = fmt.Errorf("unsupported peers value type: %T", r.Peers)
44                 return
45         }
46         cp := make(util.CompactPeers, 0, len(s)/6)
47         err = cp.UnmarshalBinary([]byte(s))
48         if err != nil {
49                 return
50         }
51         ret = make([]Peer, 0, len(cp))
52         for _, p := range cp {
53                 ret = append(ret, Peer{net.IP(p.IP[:]), int(p.Port)})
54         }
55         return
56 }
57
58 func (me *client) Announce(ar *AnnounceRequest) (ret AnnounceResponse, err error) {
59         q := make(url.Values)
60         q.Set("info_hash", string(ar.InfoHash[:]))
61         q.Set("peer_id", string(ar.PeerId[:]))
62         q.Set("port", fmt.Sprintf("%d", ar.Port))
63         q.Set("uploaded", strconv.FormatInt(ar.Uploaded, 10))
64         q.Set("downloaded", strconv.FormatInt(ar.Downloaded, 10))
65         q.Set("left", strconv.FormatUint(ar.Left, 10))
66         if ar.Event != None {
67                 q.Set("event", ar.Event.String())
68         }
69         // http://stackoverflow.com/questions/17418004/why-does-tracker-server-not-understand-my-request-bittorrent-protocol
70         q.Set("compact", "1")
71         // According to https://wiki.vuze.com/w/Message_Stream_Encryption.
72         q.Set("supportcrypto", "1")
73         var reqURL url.URL = me.url
74         reqURL.RawQuery = q.Encode()
75         resp, err := http.Get(reqURL.String())
76         if err != nil {
77                 return
78         }
79         defer resp.Body.Close()
80         buf := bytes.Buffer{}
81         io.Copy(&buf, resp.Body)
82         if resp.StatusCode != 200 {
83                 err = fmt.Errorf("response from tracker: %s: %s", resp.Status, buf.String())
84                 return
85         }
86         var trackerResponse response
87         err = bencode.Unmarshal(buf.Bytes(), &trackerResponse)
88         if err != nil {
89                 err = fmt.Errorf("error decoding %q: %s", buf.Bytes(), err)
90                 return
91         }
92         if trackerResponse.FailureReason != "" {
93                 err = errors.New(trackerResponse.FailureReason)
94                 return
95         }
96         ret.Interval = trackerResponse.Interval
97         ret.Leechers = trackerResponse.Incomplete
98         ret.Seeders = trackerResponse.Complete
99         ret.Peers, err = trackerResponse.UnmarshalPeers()
100         return
101 }
102
103 func (me *client) Connect() error {
104         // HTTP trackers do not require a connecting handshake.
105         return nil
106 }
107
108 func (me *client) String() string {
109         return me.URL()
110 }
111
112 func (me *client) URL() string {
113         return me.url.String()
114 }