Extract HTTP tracker client into separate package
This commit is contained in:
parent
8df885cd81
commit
af8c41ebe9
@ -1,7 +1 @@
|
||||
package tracker
|
||||
|
||||
import (
|
||||
"expvar"
|
||||
)
|
||||
|
||||
var vars = expvar.NewMap("tracker")
|
||||
|
@ -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
|
||||
}
|
@ -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
38
tracker/http/peer.go
Normal 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
|
||||
}
|
@ -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
10
tracker/shared/shared.go
Normal 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.
|
||||
)
|
@ -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:
|
||||
|
@ -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
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user