diff --git a/tracker/http.go b/tracker/http.go index 0f5c6c30..20678b8c 100644 --- a/tracker/http.go +++ b/tracker/http.go @@ -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()) diff --git a/tracker/tracker.go b/tracker/tracker.go index 3c663301..c57d8052 100644 --- a/tracker/tracker.go +++ b/tracker/tracker.go @@ -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) - } diff --git a/tracker/udp.go b/tracker/udp.go index 6d1228a3..07c7f0ae 100644 --- a/tracker/udp.go +++ b/tracker/udp.go @@ -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) +}