11 "github.com/anacrolix/dht/krpc"
12 "github.com/anacrolix/missinggo/httptoo"
14 "github.com/anacrolix/torrent/bencode"
17 type httpResponse struct {
18 FailureReason string `bencode:"failure reason"`
19 Interval int32 `bencode:"interval"`
20 TrackerId string `bencode:"tracker id"`
21 Complete int32 `bencode:"complete"`
22 Incomplete int32 `bencode:"incomplete"`
23 Peers Peers `bencode:"peers"`
25 Peers6 krpc.CompactIPv6NodeAddrs `bencode:"peers6"`
30 func (me *Peers) UnmarshalBencode(b []byte) (err error) {
32 err = bencode.Unmarshal(b, &_v)
36 switch v := _v.(type) {
38 vars.Add("http responses with string peers", 1)
39 var cnas krpc.CompactIPv4NodeAddrs
40 err = cnas.UnmarshalBinary([]byte(v))
44 for _, cp := range cnas {
45 *me = append(*me, Peer{
52 vars.Add("http responses with list peers", 1)
55 p.fromDictInterface(i.(map[string]interface{}))
60 vars.Add("http responses with unhandled peers type", 1)
61 err = fmt.Errorf("unsupported type: %T", _v)
66 func setAnnounceParams(_url *url.URL, ar *AnnounceRequest, opts Announce) {
69 q.Set("info_hash", string(ar.InfoHash[:]))
70 q.Set("peer_id", string(ar.PeerId[:]))
71 // AFAICT, port is mandatory, and there's no implied port key.
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))
77 q.Set("event", ar.Event.String())
79 // http://stackoverflow.com/questions/17418004/why-does-tracker-server-not-understand-my-request-bittorrent-protocol
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 if opts.ClientIp4.IP != nil {
85 q.Set("ipv4", opts.ClientIp4.String())
87 if opts.ClientIp6.IP != nil {
88 q.Set("ipv6", opts.ClientIp6.String())
90 _url.RawQuery = q.Encode()
93 func announceHTTP(opt Announce, _url *url.URL) (ret AnnounceResponse, err error) {
94 _url = httptoo.CopyURL(_url)
95 setAnnounceParams(_url, &opt.Request, opt)
96 req, err := http.NewRequest("GET", _url.String(), nil)
97 req.Header.Set("User-Agent", opt.UserAgent)
98 req.Host = opt.HostHeader
99 resp, err := opt.HttpClient.Do(req)
103 defer resp.Body.Close()
105 io.Copy(&buf, resp.Body)
106 if resp.StatusCode != 200 {
107 err = fmt.Errorf("response from tracker: %s: %s", resp.Status, buf.String())
110 var trackerResponse httpResponse
111 err = bencode.Unmarshal(buf.Bytes(), &trackerResponse)
112 if _, ok := err.(bencode.ErrUnusedTrailingBytes); ok {
114 } else if err != nil {
115 err = fmt.Errorf("error decoding %q: %s", buf.Bytes(), err)
118 if trackerResponse.FailureReason != "" {
119 err = fmt.Errorf("tracker gave failure reason: %q", trackerResponse.FailureReason)
122 vars.Add("successful http announces", 1)
123 ret.Interval = trackerResponse.Interval
124 ret.Leechers = trackerResponse.Incomplete
125 ret.Seeders = trackerResponse.Complete
126 if len(trackerResponse.Peers) != 0 {
127 vars.Add("http responses with nonempty peers key", 1)
129 ret.Peers = trackerResponse.Peers
130 if len(trackerResponse.Peers6) != 0 {
131 vars.Add("http responses with nonempty peers6 key", 1)
133 for _, na := range trackerResponse.Peers6 {
134 ret.Peers = append(ret.Peers, Peer{