15 "github.com/anacrolix/dht/v2/krpc"
16 "github.com/anacrolix/missinggo/httptoo"
18 "github.com/anacrolix/torrent/bencode"
21 type HttpResponse struct {
22 FailureReason string `bencode:"failure reason"`
23 Interval int32 `bencode:"interval"`
24 TrackerId string `bencode:"tracker id"`
25 Complete int32 `bencode:"complete"`
26 Incomplete int32 `bencode:"incomplete"`
27 Peers Peers `bencode:"peers"`
29 Peers6 krpc.CompactIPv6NodeAddrs `bencode:"peers6"`
34 func (me *Peers) UnmarshalBencode(b []byte) (err error) {
36 err = bencode.Unmarshal(b, &_v)
40 switch v := _v.(type) {
42 vars.Add("http responses with string peers", 1)
43 var cnas krpc.CompactIPv4NodeAddrs
44 err = cnas.UnmarshalBinary([]byte(v))
48 for _, cp := range cnas {
49 *me = append(*me, Peer{
56 vars.Add("http responses with list peers", 1)
59 p.FromDictInterface(i.(map[string]interface{}))
64 vars.Add("http responses with unhandled peers type", 1)
65 err = fmt.Errorf("unsupported type: %T", _v)
70 func setAnnounceParams(_url *url.URL, ar *AnnounceRequest, opts Announce) {
73 q.Set("key", strconv.FormatInt(int64(ar.Key), 10))
74 q.Set("info_hash", string(ar.InfoHash[:]))
75 q.Set("peer_id", string(ar.PeerId[:]))
76 // AFAICT, port is mandatory, and there's no implied port key.
77 q.Set("port", fmt.Sprintf("%d", ar.Port))
78 q.Set("uploaded", strconv.FormatInt(ar.Uploaded, 10))
79 q.Set("downloaded", strconv.FormatInt(ar.Downloaded, 10))
81 // The AWS S3 tracker returns "400 Bad Request: left(-1) was not in the valid range 0 -
82 // 9223372036854775807" if left is out of range, or "500 Internal Server Error: Internal Server
83 // Error" if omitted entirely.
88 q.Set("left", strconv.FormatInt(left, 10))
91 q.Set("event", ar.Event.String())
93 // http://stackoverflow.com/questions/17418004/why-does-tracker-server-not-understand-my-request-bittorrent-protocol
95 // According to https://wiki.vuze.com/w/Message_Stream_Encryption. TODO:
96 // Take EncryptionPolicy or something like it as a parameter.
97 q.Set("supportcrypto", "1")
98 doIp := func(versionKey string, ip net.IP) {
102 ipString := ip.String()
103 q.Set(versionKey, ipString)
104 // Let's try listing them. BEP 3 mentions having an "ip" param, and BEP 7 says we can list
105 // addresses for other address-families, although it's not encouraged.
106 q.Add("ip", ipString)
108 doIp("ipv4", opts.ClientIp4.IP)
109 doIp("ipv6", opts.ClientIp6.IP)
110 _url.RawQuery = q.Encode()
113 func announceHTTP(opt Announce, _url *url.URL) (ret AnnounceResponse, err error) {
114 _url = httptoo.CopyURL(_url)
115 setAnnounceParams(_url, &opt.Request, opt)
116 req, err := http.NewRequest("GET", _url.String(), nil)
117 req.Header.Set("User-Agent", opt.UserAgent)
118 req.Host = opt.HostHeader
119 if opt.Context != nil {
120 req = req.WithContext(opt.Context)
122 resp, err := (&http.Client{
123 //Timeout: time.Second * 15,
124 Transport: &http.Transport{
125 //Dial: (&net.Dialer{
126 // Timeout: 15 * time.Second,
128 Proxy: opt.HTTPProxy,
129 //TLSHandshakeTimeout: 15 * time.Second,
130 TLSClientConfig: &tls.Config{
131 InsecureSkipVerify: true,
132 ServerName: opt.ServerName,
134 // This is for S3 trackers that hold connections open.
135 DisableKeepAlives: true,
141 defer resp.Body.Close()
143 io.Copy(&buf, resp.Body)
144 if resp.StatusCode != 200 {
145 err = fmt.Errorf("response from tracker: %s: %s", resp.Status, buf.String())
148 var trackerResponse HttpResponse
149 err = bencode.Unmarshal(buf.Bytes(), &trackerResponse)
150 if _, ok := err.(bencode.ErrUnusedTrailingBytes); ok {
152 } else if err != nil {
153 err = fmt.Errorf("error decoding %q: %s", buf.Bytes(), err)
156 if trackerResponse.FailureReason != "" {
157 err = fmt.Errorf("tracker gave failure reason: %q", trackerResponse.FailureReason)
160 vars.Add("successful http announces", 1)
161 ret.Interval = trackerResponse.Interval
162 ret.Leechers = trackerResponse.Incomplete
163 ret.Seeders = trackerResponse.Complete
164 if len(trackerResponse.Peers) != 0 {
165 vars.Add("http responses with nonempty peers key", 1)
167 ret.Peers = trackerResponse.Peers
168 if len(trackerResponse.Peers6) != 0 {
169 vars.Add("http responses with nonempty peers6 key", 1)
171 for _, na := range trackerResponse.Peers6 {
172 ret.Peers = append(ret.Peers, Peer{