From af8c41ebe9f8c845251667dcbbd0874a9d973557 Mon Sep 17 00:00:00 2001 From: Matt Joiner Date: Tue, 22 Jun 2021 23:28:26 +1000 Subject: [PATCH] Extract HTTP tracker client into separate package --- tracker/expvar.go | 6 --- tracker/{ => http}/http.go | 84 ++++++++++++++++++++++----------- tracker/{ => http}/http_test.go | 5 +- tracker/http/peer.go | 38 +++++++++++++++ tracker/peer.go | 37 --------------- tracker/shared/shared.go | 10 ++++ tracker/tracker.go | 36 ++++++++------ tracker/udp.go | 3 +- 8 files changed, 130 insertions(+), 89 deletions(-) rename tracker/{ => http}/http.go (75%) rename tracker/{ => http}/http_test.go (98%) create mode 100644 tracker/http/peer.go create mode 100644 tracker/shared/shared.go diff --git a/tracker/expvar.go b/tracker/expvar.go index de5a4985..bf031739 100644 --- a/tracker/expvar.go +++ b/tracker/expvar.go @@ -1,7 +1 @@ package tracker - -import ( - "expvar" -) - -var vars = expvar.NewMap("tracker") diff --git a/tracker/http.go b/tracker/http/http.go similarity index 75% rename from tracker/http.go rename to tracker/http/http.go index eb265045..c74243cd 100644 --- a/tracker/http.go +++ b/tracker/http/http.go @@ -1,8 +1,10 @@ -package tracker +package http import ( "bytes" + "context" "crypto/tls" + "expvar" "fmt" "io" "math" @@ -13,10 +15,38 @@ import ( "github.com/anacrolix/dht/v2/krpc" "github.com/anacrolix/missinggo/httptoo" - "github.com/anacrolix/torrent/bencode" + "github.com/anacrolix/torrent/tracker/shared" + "github.com/anacrolix/torrent/tracker/udp" ) +var vars = expvar.NewMap("tracker/http") + +type Client struct { + hc *http.Client +} + +type NewClientOpts struct { + Proxy func(*http.Request) (*url.URL, error) + ServerName string +} + +func NewClient(opts NewClientOpts) Client { + return Client{ + hc: &http.Client{ + Transport: &http.Transport{ + Proxy: opts.Proxy, + TLSClientConfig: &tls.Config{ + InsecureSkipVerify: true, + ServerName: opts.ServerName, + }, + // This is for S3 trackers that hold connections open. + DisableKeepAlives: true, + }, + }, + } +} + type HttpResponse struct { FailureReason string `bencode:"failure reason"` Interval int32 `bencode:"interval"` @@ -66,7 +96,7 @@ func (me *Peers) UnmarshalBencode(b []byte) (err error) { } } -func setAnnounceParams(_url *url.URL, ar *AnnounceRequest, opts Announce) { +func setAnnounceParams(_url *url.URL, ar *AnnounceRequest, opts AnnounceOpt) { q := _url.Query() q.Set("key", strconv.FormatInt(int64(ar.Key), 10)) @@ -86,7 +116,7 @@ func setAnnounceParams(_url *url.URL, ar *AnnounceRequest, opts Announce) { } q.Set("left", strconv.FormatInt(left, 10)) - if ar.Event != None { + if ar.Event != shared.None { q.Set("event", ar.Event.String()) } // http://stackoverflow.com/questions/17418004/why-does-tracker-server-not-understand-my-request-bittorrent-protocol @@ -104,36 +134,27 @@ func setAnnounceParams(_url *url.URL, ar *AnnounceRequest, opts Announce) { // addresses for other address-families, although it's not encouraged. q.Add("ip", ipString) } - doIp("ipv4", opts.ClientIp4.IP) - doIp("ipv6", opts.ClientIp6.IP) + doIp("ipv4", opts.ClientIp4) + doIp("ipv6", opts.ClientIp6) _url.RawQuery = q.Encode() } -func announceHTTP(opt Announce, _url *url.URL) (ret AnnounceResponse, err error) { +type AnnounceOpt struct { + UserAgent string + HostHeader string + ClientIp4 net.IP + ClientIp6 net.IP +} + +type AnnounceRequest = udp.AnnounceRequest + +func (cl Client) Announce(ctx context.Context, ar AnnounceRequest, opt AnnounceOpt, _url *url.URL) (ret AnnounceResponse, err error) { _url = httptoo.CopyURL(_url) - setAnnounceParams(_url, &opt.Request, opt) - req, err := http.NewRequest("GET", _url.String(), nil) + setAnnounceParams(_url, &ar, opt) + req, err := http.NewRequestWithContext(ctx, http.MethodGet, _url.String(), nil) req.Header.Set("User-Agent", opt.UserAgent) req.Host = opt.HostHeader - if opt.Context != nil { - req = req.WithContext(opt.Context) - } - resp, err := (&http.Client{ - //Timeout: time.Second * 15, - Transport: &http.Transport{ - //Dial: (&net.Dialer{ - // Timeout: 15 * time.Second, - //}).Dial, - Proxy: opt.HTTPProxy, - //TLSHandshakeTimeout: 15 * time.Second, - TLSClientConfig: &tls.Config{ - InsecureSkipVerify: true, - ServerName: opt.ServerName, - }, - // This is for S3 trackers that hold connections open. - DisableKeepAlives: true, - }, - }).Do(req) + resp, err := cl.hc.Do(req) if err != nil { return } @@ -175,3 +196,10 @@ func announceHTTP(opt Announce, _url *url.URL) (ret AnnounceResponse, err error) } return } + +type AnnounceResponse struct { + Interval int32 // Minimum seconds the local peer should wait before next announce. + Leechers int32 + Seeders int32 + Peers []Peer +} diff --git a/tracker/http_test.go b/tracker/http/http_test.go similarity index 98% rename from tracker/http_test.go rename to tracker/http/http_test.go index 474c4aa4..f0066de7 100644 --- a/tracker/http_test.go +++ b/tracker/http/http_test.go @@ -1,12 +1,11 @@ -package tracker +package http import ( "testing" + "github.com/anacrolix/torrent/bencode" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" - - "github.com/anacrolix/torrent/bencode" ) func TestUnmarshalHTTPResponsePeerDicts(t *testing.T) { diff --git a/tracker/http/peer.go b/tracker/http/peer.go new file mode 100644 index 00000000..f715e20b --- /dev/null +++ b/tracker/http/peer.go @@ -0,0 +1,38 @@ +package http + +import ( + "fmt" + "net" + + "github.com/anacrolix/dht/v2/krpc" +) + +type Peer struct { + IP net.IP + Port int + ID []byte +} + +func (p Peer) String() string { + loc := net.JoinHostPort(p.IP.String(), fmt.Sprintf("%d", p.Port)) + if len(p.ID) != 0 { + return fmt.Sprintf("%x at %s", p.ID, loc) + } else { + return loc + } +} + +// Set from the non-compact form in BEP 3. +func (p *Peer) FromDictInterface(d map[string]interface{}) { + p.IP = net.ParseIP(d["ip"].(string)) + if _, ok := d["peer id"]; ok { + p.ID = []byte(d["peer id"].(string)) + } + p.Port = int(d["port"].(int64)) +} + +func (p Peer) FromNodeAddr(na krpc.NodeAddr) Peer { + p.IP = na.IP + p.Port = na.Port + return p +} diff --git a/tracker/peer.go b/tracker/peer.go index 3cda3dc3..bf031739 100644 --- a/tracker/peer.go +++ b/tracker/peer.go @@ -1,38 +1 @@ package tracker - -import ( - "fmt" - "net" - - "github.com/anacrolix/dht/v2/krpc" -) - -type Peer struct { - IP net.IP - Port int - ID []byte -} - -func (p Peer) String() string { - loc := net.JoinHostPort(p.IP.String(), fmt.Sprintf("%d", p.Port)) - if len(p.ID) != 0 { - return fmt.Sprintf("%x at %s", p.ID, loc) - } else { - return loc - } -} - -// Set from the non-compact form in BEP 3. -func (p *Peer) FromDictInterface(d map[string]interface{}) { - p.IP = net.ParseIP(d["ip"].(string)) - if _, ok := d["peer id"]; ok { - p.ID = []byte(d["peer id"].(string)) - } - p.Port = int(d["port"].(int64)) -} - -func (p Peer) FromNodeAddr(na krpc.NodeAddr) Peer { - p.IP = na.IP - p.Port = na.Port - return p -} diff --git a/tracker/shared/shared.go b/tracker/shared/shared.go new file mode 100644 index 00000000..7859ea99 --- /dev/null +++ b/tracker/shared/shared.go @@ -0,0 +1,10 @@ +package shared + +import "github.com/anacrolix/torrent/tracker/udp" + +const ( + None udp.AnnounceEvent = iota + Completed // The local peer just completed the torrent. + Started // The local peer has just resumed this torrent. + Stopped // The local peer is leaving the swarm. +) diff --git a/tracker/tracker.go b/tracker/tracker.go index 0a187574..66344fe2 100644 --- a/tracker/tracker.go +++ b/tracker/tracker.go @@ -8,27 +8,26 @@ import ( "time" "github.com/anacrolix/dht/v2/krpc" + trHttp "github.com/anacrolix/torrent/tracker/http" + "github.com/anacrolix/torrent/tracker/shared" "github.com/anacrolix/torrent/tracker/udp" ) +const ( + None = shared.None + Started = shared.Started + Stopped = shared.Stopped + Completed = shared.Completed +) + type AnnounceRequest = udp.AnnounceRequest -type AnnounceResponse struct { - Interval int32 // Minimum seconds the local peer should wait before next announce. - Leechers int32 - Seeders int32 - Peers []Peer -} +type AnnounceResponse = trHttp.AnnounceResponse + +type Peer = trHttp.Peer type AnnounceEvent = udp.AnnounceEvent -const ( - None AnnounceEvent = iota - Completed // The local peer just completed the torrent. - Started // The local peer has just resumed this torrent. - Stopped // The local peer is leaving the swarm. -) - var ( ErrBadScheme = errors.New("unknown scheme") ) @@ -66,7 +65,16 @@ func (me Announce) Do() (res AnnounceResponse, err error) { } switch _url.Scheme { case "http", "https": - return announceHTTP(me, _url) + cl := trHttp.NewClient(trHttp.NewClientOpts{ + Proxy: me.HTTPProxy, + ServerName: me.ServerName, + }) + return cl.Announce(me.Context, me.Request, trHttp.AnnounceOpt{ + UserAgent: me.UserAgent, + HostHeader: me.HostHeader, + ClientIp4: me.ClientIp4.IP, + ClientIp6: me.ClientIp6.IP, + }, _url) case "udp", "udp4", "udp6": return announceUDP(me, _url) default: diff --git a/tracker/udp.go b/tracker/udp.go index 4fb00b11..ac9f3e1c 100644 --- a/tracker/udp.go +++ b/tracker/udp.go @@ -8,6 +8,7 @@ import ( "github.com/anacrolix/dht/v2/krpc" "github.com/anacrolix/missinggo" + trHttp "github.com/anacrolix/torrent/tracker/http" "github.com/anacrolix/torrent/tracker/udp" ) @@ -73,7 +74,7 @@ func (c *udpAnnounce) Do(req AnnounceRequest) (res AnnounceResponse, err error) res.Leechers = h.Leechers res.Seeders = h.Seeders for _, cp := range nas.NodeAddrs() { - res.Peers = append(res.Peers, Peer{}.FromNodeAddr(cp)) + res.Peers = append(res.Peers, trHttp.Peer{}.FromNodeAddr(cp)) } return }