]> Sergey Matveev's repositories - btrtrc.git/blob - tracker/http.go
Standardize on krpc types, and extend PEX message type for IPv6
[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         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 krpc.CompactIPv4NodeAddrs
31                 err = cps.UnmarshalBinary([]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, userAgent string, 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.Header.Set("User-Agent", userAgent)
81         req.Host = host
82         resp, err := cl.Do(req)
83         if err != nil {
84                 return
85         }
86         defer resp.Body.Close()
87         var buf bytes.Buffer
88         io.Copy(&buf, resp.Body)
89         if resp.StatusCode != 200 {
90                 err = fmt.Errorf("response from tracker: %s: %s", resp.Status, buf.String())
91                 return
92         }
93         var trackerResponse httpResponse
94         err = bencode.Unmarshal(buf.Bytes(), &trackerResponse)
95         if err != nil {
96                 err = fmt.Errorf("error decoding %q: %s", buf.Bytes(), err)
97                 return
98         }
99         if trackerResponse.FailureReason != "" {
100                 err = errors.New(trackerResponse.FailureReason)
101                 return
102         }
103         ret.Interval = trackerResponse.Interval
104         ret.Leechers = trackerResponse.Incomplete
105         ret.Seeders = trackerResponse.Complete
106         ret.Peers, err = trackerResponse.UnmarshalPeers()
107         return
108 }