2
0
mirror of synced 2025-02-23 14:18:13 +00:00

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:
Matt Joiner 2016-05-20 00:48:46 +10:00
parent 8cdcbe9d40
commit 2c5eb2ae43
3 changed files with 40 additions and 66 deletions

View File

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

View File

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

View File

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