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