Also did some clean-up of the announce code, the concept of a client is going away.
"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"`
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[:]))
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())
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)
-
}
Seeders int32
}
-func init() {
- registerClientScheme("udp", newUDPClient)
-}
-
-func newUDPClient(url *url.URL) client {
- return &udpClient{
- url: *url,
- }
-}
-
func newTransactionId() int32 {
return int32(rand.Uint32())
}
return
}
-type udpClient struct {
+type udpAnnounce struct {
contiguousTimeouts int
connectionIdReceived time.Time
connectionId int64
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
// 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 {
// 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,
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
}
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)
+}