]> Sergey Matveev's repositories - btrtrc.git/blob - tracker/http.go
36cfa30313a8b9297a44cef1e532bc1db9340451
[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/dht/krpc"
13         "github.com/anacrolix/missinggo/httptoo"
14
15         "github.com/anacrolix/torrent/bencode"
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         Peers  `bencode:"peers"`
25         // BEP 7
26         Peers6 krpc.CompactIPv6NodeAddrs `bencode:"peers6"`
27 }
28
29 type Peers []Peer
30
31 func (me *Peers) UnmarshalBencode(b []byte) (err error) {
32         var _v interface{}
33         err = bencode.Unmarshal(b, &_v)
34         if err != nil {
35                 return
36         }
37         switch v := _v.(type) {
38         case string:
39                 vars.Add("http responses with string peers", 1)
40                 var cnas krpc.CompactIPv4NodeAddrs
41                 err = cnas.UnmarshalBinary([]byte(v))
42                 if err != nil {
43                         return
44                 }
45                 for _, cp := range cnas {
46                         *me = append(*me, Peer{
47                                 IP:   cp.IP[:],
48                                 Port: int(cp.Port),
49                         })
50                 }
51                 return
52         case []interface{}:
53                 vars.Add("http responses with list peers", 1)
54                 for _, i := range v {
55                         var p Peer
56                         p.fromDictInterface(i.(map[string]interface{}))
57                         *me = append(*me, p)
58                 }
59                 return
60         default:
61                 vars.Add("http responses with unhandled peers type", 1)
62                 err = fmt.Errorf("unsupported type: %T", _v)
63                 return
64         }
65 }
66
67 func setAnnounceParams(_url *url.URL, ar *AnnounceRequest) {
68         q := _url.Query()
69
70         q.Set("info_hash", string(ar.InfoHash[:]))
71         q.Set("peer_id", string(ar.PeerId[:]))
72         q.Set("port", fmt.Sprintf("%d", ar.Port))
73         q.Set("uploaded", strconv.FormatInt(ar.Uploaded, 10))
74         q.Set("downloaded", strconv.FormatInt(ar.Downloaded, 10))
75         q.Set("left", strconv.FormatUint(ar.Left, 10))
76         if ar.Event != None {
77                 q.Set("event", ar.Event.String())
78         }
79         // http://stackoverflow.com/questions/17418004/why-does-tracker-server-not-understand-my-request-bittorrent-protocol
80         q.Set("compact", "1")
81         // According to https://wiki.vuze.com/w/Message_Stream_Encryption. TODO:
82         // Take EncryptionPolicy or something like it as a parameter.
83         q.Set("supportcrypto", "1")
84
85         _url.RawQuery = q.Encode()
86 }
87
88 func announceHTTP(cl *http.Client, userAgent string, ar *AnnounceRequest, _url *url.URL, host string) (ret AnnounceResponse, err error) {
89         _url = httptoo.CopyURL(_url)
90         setAnnounceParams(_url, ar)
91         req, err := http.NewRequest("GET", _url.String(), nil)
92         req.Header.Set("User-Agent", userAgent)
93         req.Host = host
94         resp, err := cl.Do(req)
95         if err != nil {
96                 return
97         }
98         defer resp.Body.Close()
99         var buf bytes.Buffer
100         io.Copy(&buf, resp.Body)
101         if resp.StatusCode != 200 {
102                 err = fmt.Errorf("response from tracker: %s: %s", resp.Status, buf.String())
103                 return
104         }
105         var trackerResponse httpResponse
106         err = bencode.Unmarshal(buf.Bytes(), &trackerResponse)
107         if err != nil {
108                 err = fmt.Errorf("error decoding %q: %s", buf.Bytes(), err)
109                 return
110         }
111         if trackerResponse.FailureReason != "" {
112                 err = errors.New(trackerResponse.FailureReason)
113                 return
114         }
115         vars.Add("successful http announces", 1)
116         ret.Interval = trackerResponse.Interval
117         ret.Leechers = trackerResponse.Incomplete
118         ret.Seeders = trackerResponse.Complete
119         if len(trackerResponse.Peers) != 0 {
120                 vars.Add("http responses with nonempty peers key", 1)
121         }
122         ret.Peers = trackerResponse.Peers
123         if len(trackerResponse.Peers6) != 0 {
124                 vars.Add("http responses with nonempty peers6 key", 1)
125         }
126         for _, na := range trackerResponse.Peers6 {
127                 ret.Peers = append(ret.Peers, Peer{
128                         IP:   na.IP,
129                         Port: na.Port,
130                 })
131         }
132         return
133 }