tracker: Allow resolving announce URL host in advance, and passing the desired Host field for HTTP requests
Also did some clean-up of the announce code, the concept of a client is going away.
This commit is contained in:
parent
8cdcbe9d40
commit
2c5eb2ae43
@ -10,26 +10,12 @@ import (
|
||||
"net/url"
|
||||
"strconv"
|
||||
|
||||
"github.com/anacrolix/missinggo/httptoo"
|
||||
|
||||
"github.com/anacrolix/torrent/bencode"
|
||||
"github.com/anacrolix/torrent/util"
|
||||
)
|
||||
|
||||
func init() {
|
||||
registerClientScheme("http", newHTTPClient)
|
||||
}
|
||||
|
||||
type httpClient struct {
|
||||
url url.URL
|
||||
}
|
||||
|
||||
func (httpClient) Close() error { return nil }
|
||||
|
||||
func newHTTPClient(url *url.URL) client {
|
||||
return &httpClient{
|
||||
url: *url,
|
||||
}
|
||||
}
|
||||
|
||||
type httpResponse struct {
|
||||
FailureReason string `bencode:"failure reason"`
|
||||
Interval int32 `bencode:"interval"`
|
||||
@ -56,9 +42,8 @@ func (r *httpResponse) UnmarshalPeers() (ret []Peer, err error) {
|
||||
return
|
||||
}
|
||||
|
||||
func (c *httpClient) Announce(ar *AnnounceRequest) (ret AnnounceResponse, err error) {
|
||||
// retain query parameters from announce URL
|
||||
q := c.url.Query()
|
||||
func setAnnounceParams(_url *url.URL, ar *AnnounceRequest) {
|
||||
q := _url.Query()
|
||||
|
||||
q.Set("info_hash", string(ar.InfoHash[:]))
|
||||
q.Set("peer_id", string(ar.PeerId[:]))
|
||||
@ -73,14 +58,21 @@ func (c *httpClient) Announce(ar *AnnounceRequest) (ret AnnounceResponse, err er
|
||||
q.Set("compact", "1")
|
||||
// According to https://wiki.vuze.com/w/Message_Stream_Encryption.
|
||||
q.Set("supportcrypto", "1")
|
||||
var reqURL url.URL = c.url
|
||||
reqURL.RawQuery = q.Encode()
|
||||
resp, err := http.Get(reqURL.String())
|
||||
|
||||
_url.RawQuery = q.Encode()
|
||||
}
|
||||
|
||||
func announceHTTP(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.Host = host
|
||||
resp, err := http.DefaultClient.Do(req)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
buf := bytes.Buffer{}
|
||||
var buf bytes.Buffer
|
||||
io.Copy(&buf, resp.Body)
|
||||
if resp.StatusCode != 200 {
|
||||
err = fmt.Errorf("response from tracker: %s: %s", resp.Status, buf.String())
|
||||
|
@ -46,42 +46,26 @@ const (
|
||||
Stopped // The local peer is leaving the swarm.
|
||||
)
|
||||
|
||||
type client interface {
|
||||
Announce(*AnnounceRequest) (AnnounceResponse, error)
|
||||
Close() error
|
||||
}
|
||||
|
||||
var (
|
||||
ErrBadScheme = errors.New("unknown scheme")
|
||||
|
||||
schemes = make(map[string]func(*url.URL) client)
|
||||
)
|
||||
|
||||
func registerClientScheme(scheme string, newFunc func(*url.URL) client) {
|
||||
schemes[scheme] = newFunc
|
||||
func Announce(urlStr string, req *AnnounceRequest) (res AnnounceResponse, err error) {
|
||||
return AnnounceHost(urlStr, req, "")
|
||||
}
|
||||
|
||||
// Returns ErrBadScheme if the tracker scheme isn't recognised.
|
||||
func newClient(rawurl string) (cl client, err error) {
|
||||
url_s, err := url.Parse(rawurl)
|
||||
func AnnounceHost(urlStr string, req *AnnounceRequest, host string) (res AnnounceResponse, err error) {
|
||||
_url, err := url.Parse(urlStr)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
newFunc, ok := schemes[url_s.Scheme]
|
||||
if !ok {
|
||||
switch _url.Scheme {
|
||||
case "http", "https":
|
||||
return announceHTTP(req, _url, host)
|
||||
case "udp":
|
||||
return announceUDP(req, _url)
|
||||
default:
|
||||
err = ErrBadScheme
|
||||
return
|
||||
}
|
||||
cl = newFunc(url_s)
|
||||
return
|
||||
}
|
||||
|
||||
func Announce(urlStr string, req *AnnounceRequest) (res AnnounceResponse, err error) {
|
||||
cl, err := newClient(urlStr)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
defer cl.Close()
|
||||
return cl.Announce(req)
|
||||
|
||||
}
|
||||
|
@ -60,16 +60,6 @@ type AnnounceResponseHeader struct {
|
||||
Seeders int32
|
||||
}
|
||||
|
||||
func init() {
|
||||
registerClientScheme("udp", newUDPClient)
|
||||
}
|
||||
|
||||
func newUDPClient(url *url.URL) client {
|
||||
return &udpClient{
|
||||
url: *url,
|
||||
}
|
||||
}
|
||||
|
||||
func newTransactionId() int32 {
|
||||
return int32(rand.Uint32())
|
||||
}
|
||||
@ -85,7 +75,7 @@ func timeout(contiguousTimeouts int) (d time.Duration) {
|
||||
return
|
||||
}
|
||||
|
||||
type udpClient struct {
|
||||
type udpAnnounce struct {
|
||||
contiguousTimeouts int
|
||||
connectionIdReceived time.Time
|
||||
connectionId int64
|
||||
@ -93,14 +83,14 @@ type udpClient struct {
|
||||
url url.URL
|
||||
}
|
||||
|
||||
func (c *udpClient) Close() error {
|
||||
func (c *udpAnnounce) Close() error {
|
||||
if c.socket != nil {
|
||||
return c.socket.Close()
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *udpClient) Announce(req *AnnounceRequest) (res AnnounceResponse, err error) {
|
||||
func (c *udpAnnounce) Do(req *AnnounceRequest) (res AnnounceResponse, err error) {
|
||||
err = c.connect()
|
||||
if err != nil {
|
||||
return
|
||||
@ -140,7 +130,7 @@ func (c *udpClient) Announce(req *AnnounceRequest) (res AnnounceResponse, err er
|
||||
|
||||
// body is the binary serializable request body. trailer is optional data
|
||||
// following it, such as for BEP 41.
|
||||
func (c *udpClient) write(h *RequestHeader, body interface{}, trailer []byte) (err error) {
|
||||
func (c *udpAnnounce) write(h *RequestHeader, body interface{}, trailer []byte) (err error) {
|
||||
var buf bytes.Buffer
|
||||
err = binary.Write(&buf, binary.BigEndian, h)
|
||||
if err != nil {
|
||||
@ -176,7 +166,7 @@ func write(w io.Writer, data interface{}) error {
|
||||
|
||||
// args is the binary serializable request body. trailer is optional data
|
||||
// following it, such as for BEP 41.
|
||||
func (c *udpClient) request(action Action, args interface{}, options []byte) (responseBody *bytes.Buffer, err error) {
|
||||
func (c *udpAnnounce) request(action Action, args interface{}, options []byte) (responseBody *bytes.Buffer, err error) {
|
||||
tid := newTransactionId()
|
||||
err = c.write(&RequestHeader{
|
||||
ConnectionId: c.connectionId,
|
||||
@ -232,11 +222,11 @@ func readBody(r io.Reader, data ...interface{}) (err error) {
|
||||
return
|
||||
}
|
||||
|
||||
func (c *udpClient) connected() bool {
|
||||
func (c *udpAnnounce) connected() bool {
|
||||
return !c.connectionIdReceived.IsZero() && time.Now().Before(c.connectionIdReceived.Add(time.Minute))
|
||||
}
|
||||
|
||||
func (c *udpClient) connect() (err error) {
|
||||
func (c *udpAnnounce) connect() (err error) {
|
||||
if c.connected() {
|
||||
return nil
|
||||
}
|
||||
@ -266,3 +256,11 @@ func (c *udpClient) connect() (err error) {
|
||||
c.connectionIdReceived = time.Now()
|
||||
return
|
||||
}
|
||||
|
||||
func announceUDP(ar *AnnounceRequest, _url *url.URL) (AnnounceResponse, error) {
|
||||
ua := udpAnnounce{
|
||||
url: *_url,
|
||||
}
|
||||
defer ua.Close()
|
||||
return ua.Do(ar)
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user