mirror of
https://github.com/go-i2p/go-i2p-bt.git
synced 2025-07-13 20:41:28 -04:00
update tracker interface
This commit is contained in:
@ -20,6 +20,7 @@
|
|||||||
package httptracker
|
package httptracker
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"context"
|
||||||
"io"
|
"io"
|
||||||
"net/http"
|
"net/http"
|
||||||
"net/url"
|
"net/url"
|
||||||
@ -115,7 +116,7 @@ func (r AnnounceRequest) ToQuery() (vs url.Values) {
|
|||||||
if r.Port > 0 {
|
if r.Port > 0 {
|
||||||
vs.Set("port", strconv.FormatUint(uint64(r.Port), 10))
|
vs.Set("port", strconv.FormatUint(uint64(r.Port), 10))
|
||||||
}
|
}
|
||||||
if r.NumWant > 0 {
|
if r.NumWant != 0 {
|
||||||
vs.Set("numwant", strconv.FormatUint(uint64(r.NumWant), 10))
|
vs.Set("numwant", strconv.FormatUint(uint64(r.NumWant), 10))
|
||||||
}
|
}
|
||||||
if r.Key != 0 {
|
if r.Key != 0 {
|
||||||
@ -264,24 +265,28 @@ func (sr ScrapeResponse) EncodeTo(w io.Writer) (err error) {
|
|||||||
return bencode.NewEncoder(w).Encode(sr)
|
return bencode.NewEncoder(w).Encode(sr)
|
||||||
}
|
}
|
||||||
|
|
||||||
// TrackerClient represents a tracker client based on HTTP/HTTPS.
|
// Client represents a tracker client based on HTTP/HTTPS.
|
||||||
type TrackerClient struct {
|
type Client struct {
|
||||||
AnnounceURL string
|
AnnounceURL string
|
||||||
ScrapeURL string
|
ScrapeURL string
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewTrackerClient returns a new HTTPTrackerClient.
|
// NewClient returns a new HTTPClient.
|
||||||
//
|
//
|
||||||
// scrapeURL may be empty, which will replace the "announce" in announceURL
|
// scrapeURL may be empty, which will replace the "announce" in announceURL
|
||||||
// with "scrape" to generate the scrapeURL.
|
// with "scrape" to generate the scrapeURL.
|
||||||
func NewTrackerClient(announceURL, scrapeURL string) *TrackerClient {
|
func NewClient(announceURL, scrapeURL string) *Client {
|
||||||
if scrapeURL == "" {
|
if scrapeURL == "" {
|
||||||
scrapeURL = strings.Replace(announceURL, "announce", "scrape", -1)
|
scrapeURL = strings.Replace(announceURL, "announce", "scrape", -1)
|
||||||
}
|
}
|
||||||
return &TrackerClient{AnnounceURL: announceURL, ScrapeURL: scrapeURL}
|
return &Client{AnnounceURL: announceURL, ScrapeURL: scrapeURL}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (t *TrackerClient) send(u string, vs url.Values, r interface{}) (err error) {
|
// Close closes the client, which does nothing at present.
|
||||||
|
func (t *Client) Close() error { return nil }
|
||||||
|
func (t *Client) String() string { return t.AnnounceURL }
|
||||||
|
|
||||||
|
func (t *Client) send(c context.Context, u string, vs url.Values, r interface{}) (err error) {
|
||||||
sym := "?"
|
sym := "?"
|
||||||
if strings.IndexByte(u, '?') > 0 {
|
if strings.IndexByte(u, '?') > 0 {
|
||||||
sym = "&"
|
sym = "&"
|
||||||
@ -296,17 +301,19 @@ func (t *TrackerClient) send(u string, vs url.Values, r interface{}) (err error)
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Announce sends a Announce request to the tracker.
|
// Announce sends a Announce request to the tracker.
|
||||||
func (t *TrackerClient) Announce(req AnnounceRequest) (resp AnnounceResponse, err error) {
|
func (t *Client) Announce(c context.Context, req AnnounceRequest) (
|
||||||
err = t.send(t.AnnounceURL, req.ToQuery(), &resp)
|
resp AnnounceResponse, err error) {
|
||||||
|
err = t.send(c, t.AnnounceURL, req.ToQuery(), &resp)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// Scrape sends a Scrape request to the tracker.
|
// Scrape sends a Scrape request to the tracker.
|
||||||
func (t *TrackerClient) Scrape(infohashes []metainfo.Hash) (resp ScrapeResponse, err error) {
|
func (t *Client) Scrape(c context.Context, infohashes []metainfo.Hash) (
|
||||||
|
resp ScrapeResponse, err error) {
|
||||||
hs := make([]string, len(infohashes))
|
hs := make([]string, len(infohashes))
|
||||||
for i, h := range infohashes {
|
for i, h := range infohashes {
|
||||||
hs[i] = h.BytesString()
|
hs[i] = h.BytesString()
|
||||||
}
|
}
|
||||||
err = t.send(t.ScrapeURL, url.Values{"info_hash": hs}, &resp)
|
err = t.send(c, t.ScrapeURL, url.Values{"info_hash": hs}, &resp)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
@ -18,6 +18,7 @@ package tracker
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
|
"context"
|
||||||
"encoding/binary"
|
"encoding/binary"
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
@ -43,18 +44,18 @@ const (
|
|||||||
//
|
//
|
||||||
// BEP 3, 15
|
// BEP 3, 15
|
||||||
type AnnounceRequest struct {
|
type AnnounceRequest struct {
|
||||||
InfoHash metainfo.Hash
|
InfoHash metainfo.Hash // Required
|
||||||
PeerID metainfo.Hash
|
PeerID metainfo.Hash // Required
|
||||||
|
|
||||||
Uploaded int64
|
Uploaded int64 // Required, but default: 0, which should be only used for test or first.
|
||||||
Downloaded int64
|
Downloaded int64 // Required, but default: 0, which should be only used for test or first.
|
||||||
Left int64
|
Left int64 // Required, but default: 0, which should be only used for test or last.
|
||||||
Event uint32
|
Event uint32 // Required, but default: 0
|
||||||
|
|
||||||
IP net.IP
|
IP net.IP // Optional
|
||||||
Key int32
|
Key int32 // Optional
|
||||||
NumWant int32 // -1 for default
|
NumWant int32 // Optional, BEP 15: -1 for default. But we use 0 as default.
|
||||||
Port uint16
|
Port uint16 // Optional
|
||||||
}
|
}
|
||||||
|
|
||||||
// ToHTTPAnnounceRequest creates a new httptracker.AnnounceRequest from itself.
|
// ToHTTPAnnounceRequest creates a new httptracker.AnnounceRequest from itself.
|
||||||
@ -187,8 +188,10 @@ func (sr ScrapeResponse) FromUDPScrapeResponse(hs []metainfo.Hash,
|
|||||||
|
|
||||||
// Client is the interface of BT tracker client.
|
// Client is the interface of BT tracker client.
|
||||||
type Client interface {
|
type Client interface {
|
||||||
Announce(AnnounceRequest) (AnnounceResponse, error)
|
Announce(context.Context, AnnounceRequest) (AnnounceResponse, error)
|
||||||
Scrape([]metainfo.Hash) (ScrapeResponse, error)
|
Scrape(context.Context, []metainfo.Hash) (ScrapeResponse, error)
|
||||||
|
String() string
|
||||||
|
Close() error
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewClient returns a new Client.
|
// NewClient returns a new Client.
|
||||||
@ -197,16 +200,16 @@ func NewClient(connURL string) (c Client, err error) {
|
|||||||
if err == nil {
|
if err == nil {
|
||||||
switch u.Scheme {
|
switch u.Scheme {
|
||||||
case "http", "https":
|
case "http", "https":
|
||||||
c = &tclient{http: httptracker.NewTrackerClient(connURL, "")}
|
c = &tclient{url: connURL, http: httptracker.NewClient(connURL, "")}
|
||||||
case "udp", "udp4", "udp6":
|
case "udp", "udp4", "udp6":
|
||||||
var utc *udptracker.TrackerClient
|
var utc *udptracker.Client
|
||||||
utc, err = udptracker.NewTrackerClientByDial(u.Scheme, u.Host)
|
utc, err = udptracker.NewClientByDial(u.Scheme, u.Host)
|
||||||
if err == nil {
|
if err == nil {
|
||||||
var e []udptracker.Extension
|
var e []udptracker.Extension
|
||||||
if p := u.RequestURI(); p != "" {
|
if p := u.RequestURI(); p != "" {
|
||||||
e = []udptracker.Extension{udptracker.NewURLData([]byte(p))}
|
e = []udptracker.Extension{udptracker.NewURLData([]byte(p))}
|
||||||
}
|
}
|
||||||
c = &tclient{exts: e, udp: utc}
|
c = &tclient{url: connURL, exts: e, udp: utc}
|
||||||
}
|
}
|
||||||
default:
|
default:
|
||||||
err = fmt.Errorf("unknown url scheme '%s'", u.Scheme)
|
err = fmt.Errorf("unknown url scheme '%s'", u.Scheme)
|
||||||
@ -216,15 +219,25 @@ func NewClient(connURL string) (c Client, err error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type tclient struct {
|
type tclient struct {
|
||||||
http *httptracker.TrackerClient // BEP 3
|
url string
|
||||||
udp *udptracker.TrackerClient // BEP 15
|
http *httptracker.Client // BEP 3
|
||||||
exts []udptracker.Extension // BEP 41
|
udp *udptracker.Client // BEP 15
|
||||||
|
exts []udptracker.Extension // BEP 41
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *tclient) Announce(req AnnounceRequest) (resp AnnounceResponse, err error) {
|
func (c *tclient) String() string { return c.url }
|
||||||
|
|
||||||
|
func (c *tclient) Close() error {
|
||||||
|
if c.http != nil {
|
||||||
|
return c.http.Close()
|
||||||
|
}
|
||||||
|
return c.udp.Close()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *tclient) Announce(ctx context.Context, req AnnounceRequest) (resp AnnounceResponse, err error) {
|
||||||
if c.http != nil {
|
if c.http != nil {
|
||||||
var r httptracker.AnnounceResponse
|
var r httptracker.AnnounceResponse
|
||||||
if r, err = c.http.Announce(req.ToHTTPAnnounceRequest()); err != nil {
|
if r, err = c.http.Announce(ctx, req.ToHTTPAnnounceRequest()); err != nil {
|
||||||
return
|
return
|
||||||
} else if r.FailureReason != "" {
|
} else if r.FailureReason != "" {
|
||||||
err = errors.New(r.FailureReason)
|
err = errors.New(r.FailureReason)
|
||||||
@ -236,17 +249,17 @@ func (c *tclient) Announce(req AnnounceRequest) (resp AnnounceResponse, err erro
|
|||||||
|
|
||||||
r := req.ToUDPAnnounceRequest()
|
r := req.ToUDPAnnounceRequest()
|
||||||
r.Exts = c.exts
|
r.Exts = c.exts
|
||||||
rs, err := c.udp.Announce(r)
|
rs, err := c.udp.Announce(ctx, r)
|
||||||
if err == nil {
|
if err == nil {
|
||||||
resp.FromUDPAnnounceResponse(rs)
|
resp.FromUDPAnnounceResponse(rs)
|
||||||
}
|
}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *tclient) Scrape(hs []metainfo.Hash) (resp ScrapeResponse, err error) {
|
func (c *tclient) Scrape(ctx context.Context, hs []metainfo.Hash) (resp ScrapeResponse, err error) {
|
||||||
if c.http != nil {
|
if c.http != nil {
|
||||||
var r httptracker.ScrapeResponse
|
var r httptracker.ScrapeResponse
|
||||||
if r, err = c.http.Scrape(hs); err != nil {
|
if r, err = c.http.Scrape(ctx, hs); err != nil {
|
||||||
return
|
return
|
||||||
} else if r.FailureReason != "" {
|
} else if r.FailureReason != "" {
|
||||||
err = errors.New(r.FailureReason)
|
err = errors.New(r.FailureReason)
|
||||||
@ -257,7 +270,7 @@ func (c *tclient) Scrape(hs []metainfo.Hash) (resp ScrapeResponse, err error) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
r, err := c.udp.Scrape(hs)
|
r, err := c.udp.Scrape(ctx, hs)
|
||||||
if err == nil {
|
if err == nil {
|
||||||
resp = make(ScrapeResponse, len(r))
|
resp = make(ScrapeResponse, len(r))
|
||||||
resp.FromUDPScrapeResponse(hs, r)
|
resp.FromUDPScrapeResponse(hs, r)
|
||||||
|
@ -15,6 +15,7 @@
|
|||||||
package tracker
|
package tracker
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"context"
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"log"
|
"log"
|
||||||
@ -73,7 +74,7 @@ func ExampleClient() {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatal(err)
|
log.Fatal(err)
|
||||||
}
|
}
|
||||||
server := udptracker.NewTrackerServer(sconn, testHandler{})
|
server := udptracker.NewServer(sconn, testHandler{})
|
||||||
defer server.Close()
|
defer server.Close()
|
||||||
go server.Run()
|
go server.Run()
|
||||||
|
|
||||||
@ -89,7 +90,7 @@ func ExampleClient() {
|
|||||||
// Send the ANNOUNCE request to the UDP tracker server,
|
// Send the ANNOUNCE request to the UDP tracker server,
|
||||||
// and get the ANNOUNCE response.
|
// and get the ANNOUNCE response.
|
||||||
req := AnnounceRequest{IP: net.ParseIP("127.0.0.1"), Port: 80}
|
req := AnnounceRequest{IP: net.ParseIP("127.0.0.1"), Port: 80}
|
||||||
resp, err := client.Announce(req)
|
resp, err := client.Announce(context.Background(), req)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatal(err)
|
log.Fatal(err)
|
||||||
}
|
}
|
||||||
@ -106,7 +107,7 @@ func ExampleClient() {
|
|||||||
// and get the SCRAPE respsone.
|
// and get the SCRAPE respsone.
|
||||||
h1 := metainfo.Hash{1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1}
|
h1 := metainfo.Hash{1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1}
|
||||||
h2 := metainfo.Hash{2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2}
|
h2 := metainfo.Hash{2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2}
|
||||||
rs, err := client.Scrape([]metainfo.Hash{h1, h2})
|
rs, err := client.Scrape(context.Background(), []metainfo.Hash{h1, h2})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatal(err)
|
log.Fatal(err)
|
||||||
} else if len(rs) != 2 {
|
} else if len(rs) != 2 {
|
||||||
|
@ -16,6 +16,7 @@ package udptracker
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
|
"context"
|
||||||
"encoding/binary"
|
"encoding/binary"
|
||||||
"errors"
|
"errors"
|
||||||
"io"
|
"io"
|
||||||
@ -27,33 +28,30 @@ import (
|
|||||||
"github.com/xgfone/bt/metainfo"
|
"github.com/xgfone/bt/metainfo"
|
||||||
)
|
)
|
||||||
|
|
||||||
// NewTrackerClientByDial returns a new TrackerClient by dialing.
|
// NewClientByDial returns a new Client by dialing.
|
||||||
func NewTrackerClientByDial(network, address string, c ...TrackerClientConfig) (
|
func NewClientByDial(network, address string, c ...ClientConfig) (*Client, error) {
|
||||||
*TrackerClient, error) {
|
|
||||||
conn, err := net.Dial(network, address)
|
conn, err := net.Dial(network, address)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
return NewTrackerClient(conn.(*net.UDPConn), c...), nil
|
return NewClient(conn.(*net.UDPConn), c...), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewTrackerClient returns a new TrackerClient.
|
// NewClient returns a new Client.
|
||||||
func NewTrackerClient(conn *net.UDPConn, c ...TrackerClientConfig) *TrackerClient {
|
func NewClient(conn *net.UDPConn, c ...ClientConfig) *Client {
|
||||||
var conf TrackerClientConfig
|
var conf ClientConfig
|
||||||
conf.set(c...)
|
conf.set(c...)
|
||||||
ipv4 := strings.Contains(conn.LocalAddr().String(), ".")
|
ipv4 := strings.Contains(conn.LocalAddr().String(), ".")
|
||||||
return &TrackerClient{conn: conn, conf: conf, ipv4: ipv4}
|
return &Client{conn: conn, conf: conf, ipv4: ipv4}
|
||||||
}
|
}
|
||||||
|
|
||||||
// TrackerClientConfig is used to configure the TrackerClient.
|
// ClientConfig is used to configure the Client.
|
||||||
type TrackerClientConfig struct {
|
type ClientConfig struct {
|
||||||
// ReadTimeout is used to receive the response.
|
MaxBufSize int // Default: 2048
|
||||||
ReadTimeout time.Duration // Default: 5s
|
|
||||||
MaxBufSize int // Default: 2048
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *TrackerClientConfig) set(conf ...TrackerClientConfig) {
|
func (c *ClientConfig) set(conf ...ClientConfig) {
|
||||||
if len(conf) > 0 {
|
if len(conf) > 0 {
|
||||||
*c = conf[0]
|
*c = conf[0]
|
||||||
}
|
}
|
||||||
@ -61,20 +59,17 @@ func (c *TrackerClientConfig) set(conf ...TrackerClientConfig) {
|
|||||||
if c.MaxBufSize <= 0 {
|
if c.MaxBufSize <= 0 {
|
||||||
c.MaxBufSize = 2048
|
c.MaxBufSize = 2048
|
||||||
}
|
}
|
||||||
if c.ReadTimeout <= 0 {
|
|
||||||
c.ReadTimeout = time.Second * 5
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// TrackerClient is a tracker client based on UDP.
|
// Client is a tracker client based on UDP.
|
||||||
//
|
//
|
||||||
// Notice: the request is synchronized, that's, the last request is not returned,
|
// Notice: the request is synchronized, that's, the last request is not returned,
|
||||||
// the next request must not be sent.
|
// the next request must not be sent.
|
||||||
//
|
//
|
||||||
// BEP 15
|
// BEP 15
|
||||||
type TrackerClient struct {
|
type Client struct {
|
||||||
ipv4 bool
|
ipv4 bool
|
||||||
conf TrackerClientConfig
|
conf ClientConfig
|
||||||
conn *net.UDPConn
|
conn *net.UDPConn
|
||||||
last time.Time
|
last time.Time
|
||||||
cid uint64
|
cid uint64
|
||||||
@ -82,24 +77,39 @@ type TrackerClient struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Close closes the UDP tracker client.
|
// Close closes the UDP tracker client.
|
||||||
func (utc *TrackerClient) Close() { utc.conn.Close() }
|
func (utc *Client) Close() error { return utc.conn.Close() }
|
||||||
|
func (utc *Client) String() string { return utc.conn.RemoteAddr().String() }
|
||||||
|
|
||||||
func (utc *TrackerClient) readResp(b []byte) (int, error) {
|
func (utc *Client) readResp(ctx context.Context, b []byte) (int, error) {
|
||||||
utc.conn.SetReadDeadline(time.Now().Add(utc.conf.ReadTimeout))
|
done := make(chan struct{})
|
||||||
return utc.conn.Read(b)
|
|
||||||
|
var n int
|
||||||
|
var err error
|
||||||
|
go func() {
|
||||||
|
n, err = utc.conn.Read(b)
|
||||||
|
close(done)
|
||||||
|
}()
|
||||||
|
|
||||||
|
select {
|
||||||
|
case <-ctx.Done():
|
||||||
|
utc.conn.SetReadDeadline(time.Now())
|
||||||
|
return n, ctx.Err()
|
||||||
|
case <-done:
|
||||||
|
return n, err
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (utc *TrackerClient) getTranID() uint32 {
|
func (utc *Client) getTranID() uint32 {
|
||||||
return atomic.AddUint32(&utc.tid, 1)
|
return atomic.AddUint32(&utc.tid, 1)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (utc *TrackerClient) parseError(b []byte) (tid uint32, reason string) {
|
func (utc *Client) parseError(b []byte) (tid uint32, reason string) {
|
||||||
tid = binary.BigEndian.Uint32(b[:4])
|
tid = binary.BigEndian.Uint32(b[:4])
|
||||||
reason = string(b[4:])
|
reason = string(b[4:])
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
func (utc *TrackerClient) send(b []byte) (err error) {
|
func (utc *Client) send(b []byte) (err error) {
|
||||||
n, err := utc.conn.Write(b)
|
n, err := utc.conn.Write(b)
|
||||||
if err == nil && n < len(b) {
|
if err == nil && n < len(b) {
|
||||||
err = io.ErrShortWrite
|
err = io.ErrShortWrite
|
||||||
@ -107,7 +117,7 @@ func (utc *TrackerClient) send(b []byte) (err error) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
func (utc *TrackerClient) connect() (err error) {
|
func (utc *Client) connect(ctx context.Context) (err error) {
|
||||||
tid := utc.getTranID()
|
tid := utc.getTranID()
|
||||||
buf := bytes.NewBuffer(make([]byte, 0, 16))
|
buf := bytes.NewBuffer(make([]byte, 0, 16))
|
||||||
binary.Write(buf, binary.BigEndian, ProtocolID)
|
binary.Write(buf, binary.BigEndian, ProtocolID)
|
||||||
@ -118,7 +128,7 @@ func (utc *TrackerClient) connect() (err error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
data := make([]byte, 32)
|
data := make([]byte, 32)
|
||||||
n, err := utc.readResp(data)
|
n, err := utc.readResp(ctx, data)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return
|
return
|
||||||
} else if n < 8 {
|
} else if n < 8 {
|
||||||
@ -148,18 +158,19 @@ func (utc *TrackerClient) connect() (err error) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
func (utc *TrackerClient) getConnectionID() (cid uint64, err error) {
|
func (utc *Client) getConnectionID(ctx context.Context) (cid uint64, err error) {
|
||||||
cid = utc.cid
|
cid = utc.cid
|
||||||
if time.Now().Sub(utc.last) > time.Minute {
|
if time.Now().Sub(utc.last) > time.Minute {
|
||||||
if err = utc.connect(); err == nil {
|
if err = utc.connect(ctx); err == nil {
|
||||||
cid = utc.cid
|
cid = utc.cid
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
func (utc *TrackerClient) announce(req AnnounceRequest) (r AnnounceResponse, err error) {
|
func (utc *Client) announce(ctx context.Context, req AnnounceRequest) (
|
||||||
cid, err := utc.getConnectionID()
|
r AnnounceResponse, err error) {
|
||||||
|
cid, err := utc.getConnectionID(ctx)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@ -176,7 +187,7 @@ func (utc *TrackerClient) announce(req AnnounceRequest) (r AnnounceResponse, err
|
|||||||
}
|
}
|
||||||
|
|
||||||
data := make([]byte, utc.conf.MaxBufSize)
|
data := make([]byte, utc.conf.MaxBufSize)
|
||||||
n, err := utc.readResp(data)
|
n, err := utc.readResp(ctx, data)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return
|
return
|
||||||
} else if n < 8 {
|
} else if n < 8 {
|
||||||
@ -217,22 +228,23 @@ func (utc *TrackerClient) announce(req AnnounceRequest) (r AnnounceResponse, err
|
|||||||
// then send the ANNOUNCE request.
|
// then send the ANNOUNCE request.
|
||||||
// 2. If returning an error, you should retry it.
|
// 2. If returning an error, you should retry it.
|
||||||
// See http://www.bittorrent.org/beps/bep_0015.html#time-outs
|
// See http://www.bittorrent.org/beps/bep_0015.html#time-outs
|
||||||
func (utc *TrackerClient) Announce(r AnnounceRequest) (AnnounceResponse, error) {
|
func (utc *Client) Announce(c context.Context, r AnnounceRequest) (AnnounceResponse, error) {
|
||||||
return utc.announce(r)
|
return utc.announce(c, r)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (utc *TrackerClient) scrape(infohashes []metainfo.Hash) (rs []ScrapeResponse, err error) {
|
func (utc *Client) scrape(c context.Context, ihs []metainfo.Hash) (
|
||||||
cid, err := utc.getConnectionID()
|
rs []ScrapeResponse, err error) {
|
||||||
|
cid, err := utc.getConnectionID(c)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
tid := utc.getTranID()
|
tid := utc.getTranID()
|
||||||
buf := bytes.NewBuffer(make([]byte, 0, 16+len(infohashes)*20))
|
buf := bytes.NewBuffer(make([]byte, 0, 16+len(ihs)*20))
|
||||||
binary.Write(buf, binary.BigEndian, cid)
|
binary.Write(buf, binary.BigEndian, cid)
|
||||||
binary.Write(buf, binary.BigEndian, ActionScrape)
|
binary.Write(buf, binary.BigEndian, ActionScrape)
|
||||||
binary.Write(buf, binary.BigEndian, tid)
|
binary.Write(buf, binary.BigEndian, tid)
|
||||||
for _, h := range infohashes {
|
for _, h := range ihs {
|
||||||
buf.Write(h[:])
|
buf.Write(h[:])
|
||||||
}
|
}
|
||||||
if err = utc.send(buf.Bytes()); err != nil {
|
if err = utc.send(buf.Bytes()); err != nil {
|
||||||
@ -240,7 +252,7 @@ func (utc *TrackerClient) scrape(infohashes []metainfo.Hash) (rs []ScrapeRespons
|
|||||||
}
|
}
|
||||||
|
|
||||||
data := make([]byte, utc.conf.MaxBufSize)
|
data := make([]byte, utc.conf.MaxBufSize)
|
||||||
n, err := utc.readResp(data)
|
n, err := utc.readResp(c, data)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return
|
return
|
||||||
} else if n < 8 {
|
} else if n < 8 {
|
||||||
@ -284,6 +296,6 @@ func (utc *TrackerClient) scrape(infohashes []metainfo.Hash) (rs []ScrapeRespons
|
|||||||
// then send the ANNOUNCE request.
|
// then send the ANNOUNCE request.
|
||||||
// 2. If returning an error, you should retry it.
|
// 2. If returning an error, you should retry it.
|
||||||
// See http://www.bittorrent.org/beps/bep_0015.html#time-outs
|
// See http://www.bittorrent.org/beps/bep_0015.html#time-outs
|
||||||
func (utc *TrackerClient) Scrape(hs []metainfo.Hash) ([]ScrapeResponse, error) {
|
func (utc *Client) Scrape(c context.Context, hs []metainfo.Hash) ([]ScrapeResponse, error) {
|
||||||
return utc.scrape(hs)
|
return utc.scrape(c, hs)
|
||||||
}
|
}
|
||||||
|
@ -27,8 +27,8 @@ import (
|
|||||||
"github.com/xgfone/bt/metainfo"
|
"github.com/xgfone/bt/metainfo"
|
||||||
)
|
)
|
||||||
|
|
||||||
// TrackerServerHandler is used to handle the request from the client.
|
// ServerHandler is used to handle the request from the client.
|
||||||
type TrackerServerHandler interface {
|
type ServerHandler interface {
|
||||||
// OnConnect is used to check whether to make the connection or not.
|
// OnConnect is used to check whether to make the connection or not.
|
||||||
OnConnect(raddr *net.UDPAddr) (err error)
|
OnConnect(raddr *net.UDPAddr) (err error)
|
||||||
OnAnnounce(raddr *net.UDPAddr, req AnnounceRequest) (AnnounceResponse, error)
|
OnAnnounce(raddr *net.UDPAddr, req AnnounceRequest) (AnnounceResponse, error)
|
||||||
@ -45,13 +45,13 @@ type wrappedPeerAddr struct {
|
|||||||
Time time.Time
|
Time time.Time
|
||||||
}
|
}
|
||||||
|
|
||||||
// TrackerServerConfig is used to configure the TrackerServer.
|
// ServerConfig is used to configure the Server.
|
||||||
type TrackerServerConfig struct {
|
type ServerConfig struct {
|
||||||
MaxBufSize int // Default: 2048
|
MaxBufSize int // Default: 2048
|
||||||
ErrorLog func(format string, args ...interface{}) // Default: log.Printf
|
ErrorLog func(format string, args ...interface{}) // Default: log.Printf
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *TrackerServerConfig) setDefault() {
|
func (c *ServerConfig) setDefault() {
|
||||||
if c.MaxBufSize <= 0 {
|
if c.MaxBufSize <= 0 {
|
||||||
c.MaxBufSize = 2048
|
c.MaxBufSize = 2048
|
||||||
}
|
}
|
||||||
@ -60,11 +60,11 @@ func (c *TrackerServerConfig) setDefault() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// TrackerServer is a tracker server based on UDP.
|
// Server is a tracker server based on UDP.
|
||||||
type TrackerServer struct {
|
type Server struct {
|
||||||
conn net.PacketConn
|
conn net.PacketConn
|
||||||
conf TrackerServerConfig
|
conf ServerConfig
|
||||||
handler TrackerServerHandler
|
handler ServerHandler
|
||||||
bufpool sync.Pool
|
bufpool sync.Pool
|
||||||
|
|
||||||
cid uint64
|
cid uint64
|
||||||
@ -73,16 +73,15 @@ type TrackerServer struct {
|
|||||||
conns map[uint64]wrappedPeerAddr
|
conns map[uint64]wrappedPeerAddr
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewTrackerServer returns a new TrackerServer.
|
// NewServer returns a new Server.
|
||||||
func NewTrackerServer(c net.PacketConn, h TrackerServerHandler,
|
func NewServer(c net.PacketConn, h ServerHandler, sc ...ServerConfig) *Server {
|
||||||
config ...TrackerServerConfig) *TrackerServer {
|
var conf ServerConfig
|
||||||
var conf TrackerServerConfig
|
if len(sc) > 0 {
|
||||||
if len(config) > 0 {
|
conf = sc[0]
|
||||||
conf = config[0]
|
|
||||||
}
|
}
|
||||||
conf.setDefault()
|
conf.setDefault()
|
||||||
|
|
||||||
s := &TrackerServer{
|
s := &Server{
|
||||||
conf: conf,
|
conf: conf,
|
||||||
conn: c,
|
conn: c,
|
||||||
handler: h,
|
handler: h,
|
||||||
@ -95,7 +94,7 @@ func NewTrackerServer(c net.PacketConn, h TrackerServerHandler,
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Close closes the tracker server.
|
// Close closes the tracker server.
|
||||||
func (uts *TrackerServer) Close() {
|
func (uts *Server) Close() {
|
||||||
select {
|
select {
|
||||||
case <-uts.exit:
|
case <-uts.exit:
|
||||||
default:
|
default:
|
||||||
@ -104,7 +103,7 @@ func (uts *TrackerServer) Close() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (uts *TrackerServer) cleanConnectionID(interval time.Duration) {
|
func (uts *Server) cleanConnectionID(interval time.Duration) {
|
||||||
tick := time.NewTicker(interval)
|
tick := time.NewTicker(interval)
|
||||||
defer tick.Stop()
|
defer tick.Stop()
|
||||||
for {
|
for {
|
||||||
@ -124,7 +123,7 @@ func (uts *TrackerServer) cleanConnectionID(interval time.Duration) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Run starts the tracker server.
|
// Run starts the tracker server.
|
||||||
func (uts *TrackerServer) Run() {
|
func (uts *Server) Run() {
|
||||||
go uts.cleanConnectionID(time.Minute * 2)
|
go uts.cleanConnectionID(time.Minute * 2)
|
||||||
for {
|
for {
|
||||||
buf := uts.bufpool.Get().([]byte)
|
buf := uts.bufpool.Get().([]byte)
|
||||||
@ -141,12 +140,12 @@ func (uts *TrackerServer) Run() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (uts *TrackerServer) handleRequest(raddr *net.UDPAddr, buf []byte, n int) {
|
func (uts *Server) handleRequest(raddr *net.UDPAddr, buf []byte, n int) {
|
||||||
defer uts.bufpool.Put(buf)
|
defer uts.bufpool.Put(buf)
|
||||||
uts.handlePacket(raddr, buf[:n])
|
uts.handlePacket(raddr, buf[:n])
|
||||||
}
|
}
|
||||||
|
|
||||||
func (uts *TrackerServer) send(raddr *net.UDPAddr, b []byte) {
|
func (uts *Server) send(raddr *net.UDPAddr, b []byte) {
|
||||||
n, err := uts.conn.WriteTo(b, raddr)
|
n, err := uts.conn.WriteTo(b, raddr)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
uts.conf.ErrorLog("fail to send the udp tracker response to '%s': %s",
|
uts.conf.ErrorLog("fail to send the udp tracker response to '%s': %s",
|
||||||
@ -156,18 +155,18 @@ func (uts *TrackerServer) send(raddr *net.UDPAddr, b []byte) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (uts *TrackerServer) getConnectionID() uint64 {
|
func (uts *Server) getConnectionID() uint64 {
|
||||||
return atomic.AddUint64(&uts.cid, 1)
|
return atomic.AddUint64(&uts.cid, 1)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (uts *TrackerServer) addConnection(cid uint64, raddr *net.UDPAddr) {
|
func (uts *Server) addConnection(cid uint64, raddr *net.UDPAddr) {
|
||||||
now := time.Now()
|
now := time.Now()
|
||||||
uts.lock.Lock()
|
uts.lock.Lock()
|
||||||
uts.conns[cid] = wrappedPeerAddr{Addr: raddr, Time: now}
|
uts.conns[cid] = wrappedPeerAddr{Addr: raddr, Time: now}
|
||||||
uts.lock.Unlock()
|
uts.lock.Unlock()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (uts *TrackerServer) checkConnection(cid uint64, raddr *net.UDPAddr) (ok bool) {
|
func (uts *Server) checkConnection(cid uint64, raddr *net.UDPAddr) (ok bool) {
|
||||||
uts.lock.RLock()
|
uts.lock.RLock()
|
||||||
if w, _ok := uts.conns[cid]; _ok && w.Addr.Port == raddr.Port &&
|
if w, _ok := uts.conns[cid]; _ok && w.Addr.Port == raddr.Port &&
|
||||||
bytes.Equal(w.Addr.IP, raddr.IP) {
|
bytes.Equal(w.Addr.IP, raddr.IP) {
|
||||||
@ -177,21 +176,21 @@ func (uts *TrackerServer) checkConnection(cid uint64, raddr *net.UDPAddr) (ok bo
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
func (uts *TrackerServer) sendError(raddr *net.UDPAddr, tid uint32, reason string) {
|
func (uts *Server) sendError(raddr *net.UDPAddr, tid uint32, reason string) {
|
||||||
buf := bytes.NewBuffer(make([]byte, 0, 8+len(reason)))
|
buf := bytes.NewBuffer(make([]byte, 0, 8+len(reason)))
|
||||||
encodeResponseHeader(buf, ActionError, tid)
|
encodeResponseHeader(buf, ActionError, tid)
|
||||||
buf.WriteString(reason)
|
buf.WriteString(reason)
|
||||||
uts.send(raddr, buf.Bytes())
|
uts.send(raddr, buf.Bytes())
|
||||||
}
|
}
|
||||||
|
|
||||||
func (uts *TrackerServer) sendConnResp(raddr *net.UDPAddr, tid uint32, cid uint64) {
|
func (uts *Server) sendConnResp(raddr *net.UDPAddr, tid uint32, cid uint64) {
|
||||||
buf := bytes.NewBuffer(make([]byte, 0, 16))
|
buf := bytes.NewBuffer(make([]byte, 0, 16))
|
||||||
encodeResponseHeader(buf, ActionConnect, tid)
|
encodeResponseHeader(buf, ActionConnect, tid)
|
||||||
binary.Write(buf, binary.BigEndian, cid)
|
binary.Write(buf, binary.BigEndian, cid)
|
||||||
uts.send(raddr, buf.Bytes())
|
uts.send(raddr, buf.Bytes())
|
||||||
}
|
}
|
||||||
|
|
||||||
func (uts *TrackerServer) sendAnnounceResp(raddr *net.UDPAddr, tid uint32,
|
func (uts *Server) sendAnnounceResp(raddr *net.UDPAddr, tid uint32,
|
||||||
resp AnnounceResponse) {
|
resp AnnounceResponse) {
|
||||||
buf := bytes.NewBuffer(make([]byte, 0, 8+12+len(resp.Addresses)*18))
|
buf := bytes.NewBuffer(make([]byte, 0, 8+12+len(resp.Addresses)*18))
|
||||||
encodeResponseHeader(buf, ActionAnnounce, tid)
|
encodeResponseHeader(buf, ActionAnnounce, tid)
|
||||||
@ -199,7 +198,7 @@ func (uts *TrackerServer) sendAnnounceResp(raddr *net.UDPAddr, tid uint32,
|
|||||||
uts.send(raddr, buf.Bytes())
|
uts.send(raddr, buf.Bytes())
|
||||||
}
|
}
|
||||||
|
|
||||||
func (uts *TrackerServer) sendScrapResp(raddr *net.UDPAddr, tid uint32,
|
func (uts *Server) sendScrapResp(raddr *net.UDPAddr, tid uint32,
|
||||||
rs []ScrapeResponse) {
|
rs []ScrapeResponse) {
|
||||||
buf := bytes.NewBuffer(make([]byte, 0, 8+len(rs)*12))
|
buf := bytes.NewBuffer(make([]byte, 0, 8+len(rs)*12))
|
||||||
encodeResponseHeader(buf, ActionScrape, tid)
|
encodeResponseHeader(buf, ActionScrape, tid)
|
||||||
@ -209,7 +208,7 @@ func (uts *TrackerServer) sendScrapResp(raddr *net.UDPAddr, tid uint32,
|
|||||||
uts.send(raddr, buf.Bytes())
|
uts.send(raddr, buf.Bytes())
|
||||||
}
|
}
|
||||||
|
|
||||||
func (uts *TrackerServer) handlePacket(raddr *net.UDPAddr, b []byte) {
|
func (uts *Server) handlePacket(raddr *net.UDPAddr, b []byte) {
|
||||||
cid := binary.BigEndian.Uint64(b[:8])
|
cid := binary.BigEndian.Uint64(b[:8])
|
||||||
action := binary.BigEndian.Uint32(b[8:12])
|
action := binary.BigEndian.Uint32(b[8:12])
|
||||||
tid := binary.BigEndian.Uint32(b[12:16])
|
tid := binary.BigEndian.Uint32(b[12:16])
|
||||||
|
@ -15,6 +15,7 @@
|
|||||||
package udptracker
|
package udptracker
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"context"
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"log"
|
"log"
|
||||||
@ -66,13 +67,13 @@ func (testHandler) OnScrap(raddr *net.UDPAddr, infohashes []metainfo.Hash) (
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
func ExampleTrackerClient() {
|
func ExampleClient() {
|
||||||
// Start the UDP tracker server
|
// Start the UDP tracker server
|
||||||
sconn, err := net.ListenPacket("udp4", "127.0.0.1:8001")
|
sconn, err := net.ListenPacket("udp4", "127.0.0.1:8001")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatal(err)
|
log.Fatal(err)
|
||||||
}
|
}
|
||||||
server := NewTrackerServer(sconn, testHandler{})
|
server := NewServer(sconn, testHandler{})
|
||||||
defer server.Close()
|
defer server.Close()
|
||||||
go server.Run()
|
go server.Run()
|
||||||
|
|
||||||
@ -80,7 +81,7 @@ func ExampleTrackerClient() {
|
|||||||
time.Sleep(time.Second)
|
time.Sleep(time.Second)
|
||||||
|
|
||||||
// Create a client and dial to the UDP tracker server.
|
// Create a client and dial to the UDP tracker server.
|
||||||
client, err := NewTrackerClientByDial("udp4", "127.0.0.1:8001")
|
client, err := NewClientByDial("udp4", "127.0.0.1:8001")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatal(err)
|
log.Fatal(err)
|
||||||
}
|
}
|
||||||
@ -89,7 +90,7 @@ func ExampleTrackerClient() {
|
|||||||
// and get the ANNOUNCE response.
|
// and get the ANNOUNCE response.
|
||||||
exts := []Extension{NewURLData([]byte("data")), NewNop()}
|
exts := []Extension{NewURLData([]byte("data")), NewNop()}
|
||||||
req := AnnounceRequest{IP: net.ParseIP("127.0.0.1"), Port: 80, Exts: exts}
|
req := AnnounceRequest{IP: net.ParseIP("127.0.0.1"), Port: 80, Exts: exts}
|
||||||
resp, err := client.Announce(req)
|
resp, err := client.Announce(context.Background(), req)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatal(err)
|
log.Fatal(err)
|
||||||
}
|
}
|
||||||
@ -105,7 +106,7 @@ func ExampleTrackerClient() {
|
|||||||
// Send the SCRAPE request to the UDP tracker server,
|
// Send the SCRAPE request to the UDP tracker server,
|
||||||
// and get the SCRAPE respsone.
|
// and get the SCRAPE respsone.
|
||||||
hs := []metainfo.Hash{metainfo.NewRandomHash(), metainfo.NewRandomHash()}
|
hs := []metainfo.Hash{metainfo.NewRandomHash(), metainfo.NewRandomHash()}
|
||||||
rs, err := client.Scrape(hs)
|
rs, err := client.Scrape(context.Background(), hs)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatal(err)
|
log.Fatal(err)
|
||||||
} else if len(rs) != 2 {
|
} else if len(rs) != 2 {
|
||||||
|
Reference in New Issue
Block a user