2
0
mirror of synced 2025-02-23 06:08:07 +00:00

Extract HTTP tracker client into separate package

This commit is contained in:
Matt Joiner 2021-06-22 23:28:26 +10:00
parent 8df885cd81
commit af8c41ebe9
8 changed files with 130 additions and 89 deletions

View File

@ -1,7 +1 @@
package tracker
import (
"expvar"
)
var vars = expvar.NewMap("tracker")

View File

@ -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
}

View File

@ -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) {

38
tracker/http/peer.go Normal file
View File

@ -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
}

View File

@ -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
}

10
tracker/shared/shared.go Normal file
View File

@ -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.
)

View File

@ -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:

View File

@ -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
}