torrent/tracker/http.go

134 lines
3.4 KiB
Go
Raw Normal View History

package tracker
2015-03-26 06:20:31 +00:00
import (
"bytes"
"errors"
"fmt"
"io"
"net/http"
"net/url"
"strconv"
"github.com/anacrolix/dht/krpc"
"github.com/anacrolix/missinggo/httptoo"
"github.com/anacrolix/torrent/bencode"
2015-03-26 06:20:31 +00:00
)
2016-02-07 06:45:46 +00:00
type httpResponse struct {
FailureReason string `bencode:"failure reason"`
Interval int32 `bencode:"interval"`
TrackerId string `bencode:"tracker id"`
Complete int32 `bencode:"complete"`
Incomplete int32 `bencode:"incomplete"`
Peers Peers `bencode:"peers"`
// BEP 7
Peers6 krpc.CompactIPv6NodeAddrs `bencode:"peers6"`
2015-03-26 06:20:31 +00:00
}
type Peers []Peer
func (me *Peers) UnmarshalBencode(b []byte) (err error) {
var _v interface{}
err = bencode.Unmarshal(b, &_v)
if err != nil {
return
}
switch v := _v.(type) {
case string:
vars.Add("http responses with string peers", 1)
var cnas krpc.CompactIPv4NodeAddrs
err = cnas.UnmarshalBinary([]byte(v))
if err != nil {
return
}
for _, cp := range cnas {
*me = append(*me, Peer{
IP: cp.IP[:],
Port: int(cp.Port),
})
}
2015-03-26 06:20:31 +00:00
return
case []interface{}:
vars.Add("http responses with list peers", 1)
for _, i := range v {
var p Peer
p.fromDictInterface(i.(map[string]interface{}))
*me = append(*me, p)
}
return
default:
vars.Add("http responses with unhandled peers type", 1)
err = fmt.Errorf("unsupported type: %T", _v)
2015-03-26 06:20:31 +00:00
return
}
}
func setAnnounceParams(_url *url.URL, ar *AnnounceRequest) {
q := _url.Query()
2015-03-26 06:20:31 +00:00
q.Set("info_hash", string(ar.InfoHash[:]))
q.Set("peer_id", string(ar.PeerId[:]))
q.Set("port", fmt.Sprintf("%d", ar.Port))
q.Set("uploaded", strconv.FormatInt(ar.Uploaded, 10))
q.Set("downloaded", strconv.FormatInt(ar.Downloaded, 10))
q.Set("left", strconv.FormatUint(ar.Left, 10))
if ar.Event != None {
2015-03-26 06:20:31 +00:00
q.Set("event", ar.Event.String())
}
// http://stackoverflow.com/questions/17418004/why-does-tracker-server-not-understand-my-request-bittorrent-protocol
q.Set("compact", "1")
2018-02-13 00:18:23 +00:00
// According to https://wiki.vuze.com/w/Message_Stream_Encryption. TODO:
// Take EncryptionPolicy or something like it as a parameter.
2015-03-27 06:22:42 +00:00
q.Set("supportcrypto", "1")
_url.RawQuery = q.Encode()
}
func announceHTTP(cl *http.Client, userAgent string, ar *AnnounceRequest, _url *url.URL, host string) (ret AnnounceResponse, err error) {
_url = httptoo.CopyURL(_url)
setAnnounceParams(_url, ar)
req, err := http.NewRequest("GET", _url.String(), nil)
req.Header.Set("User-Agent", userAgent)
req.Host = host
resp, err := cl.Do(req)
2015-03-26 06:20:31 +00:00
if err != nil {
return
}
defer resp.Body.Close()
var buf bytes.Buffer
2015-03-26 06:20:31 +00:00
io.Copy(&buf, resp.Body)
if resp.StatusCode != 200 {
err = fmt.Errorf("response from tracker: %s: %s", resp.Status, buf.String())
return
}
2016-02-07 06:45:46 +00:00
var trackerResponse httpResponse
err = bencode.Unmarshal(buf.Bytes(), &trackerResponse)
2015-03-26 06:20:31 +00:00
if err != nil {
err = fmt.Errorf("error decoding %q: %s", buf.Bytes(), err)
2015-03-26 06:20:31 +00:00
return
}
if trackerResponse.FailureReason != "" {
err = errors.New(trackerResponse.FailureReason)
return
}
vars.Add("successful http announces", 1)
2015-03-26 06:20:31 +00:00
ret.Interval = trackerResponse.Interval
ret.Leechers = trackerResponse.Incomplete
ret.Seeders = trackerResponse.Complete
if len(trackerResponse.Peers) != 0 {
vars.Add("http responses with nonempty peers key", 1)
}
ret.Peers = trackerResponse.Peers
if len(trackerResponse.Peers6) != 0 {
vars.Add("http responses with nonempty peers6 key", 1)
}
for _, na := range trackerResponse.Peers6 {
ret.Peers = append(ret.Peers, Peer{
IP: na.IP,
Port: na.Port,
})
}
2015-03-26 06:20:31 +00:00
return
}