package tracker
import (
+ "errors"
"net"
"net/url"
)
const (
None AnnounceEvent = iota
+ Completed
+ Started
+ Stopped
)
type Client interface {
Announce(*AnnounceRequest) (AnnounceResponse, error)
+ Connect() error
}
-var schemes = make(map[string]func(*url.URL) Client)
+var (
+ ErrNotConnected = errors.New("not connected")
+ 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 New(url *url.URL) Client {
- return schemes[url.Scheme](url)
+func New(url *url.URL) (cl Client, err error) {
+ newFunc, ok := schemes[url.Scheme]
+ if !ok {
+ err = ErrBadScheme
+ return
+ }
+ cl = newFunc(url)
+ return
}
"bitbucket.org/anacrolix/go.torrent/tracker"
"bytes"
"encoding/binary"
+ "errors"
"io"
"math/rand"
"net"
}
func newClient(url *url.URL) tracker.Client {
- return &client{}
+ return &client{
+ url: url,
+ }
}
func newTransactionId() int32 {
connectionIdReceived time.Time
connectionId int64
socket net.Conn
+ url *url.URL
}
func (c *client) Announce(req *tracker.AnnounceRequest) (res tracker.AnnounceResponse, err error) {
- err = c.connect()
- if err != nil {
+ if !c.connected() {
+ err = tracker.ErrNotConnected
return
}
b, err := c.request(Announce, req)
if err != nil {
panic(err)
}
- err = binary.Write(buf, binary.BigEndian, body)
- if err != nil {
- panic(err)
+ if body != nil {
+ err = binary.Write(buf, binary.BigEndian, body)
+ if err != nil {
+ panic(err)
+ }
}
n, err := c.socket.Write(buf.Bytes())
if err != nil {
default:
return
}
- if h.Action != action {
- continue
- }
if h.TransactionId != tid {
continue
}
return
}
-func (c *client) connect() (err error) {
- if !c.connectionIdReceived.IsZero() && time.Now().Before(c.connectionIdReceived.Add(time.Minute)) {
+func (c *client) connected() bool {
+ return !c.connectionIdReceived.IsZero() && time.Now().Before(c.connectionIdReceived.Add(time.Minute))
+}
+
+func (c *client) Connect() (err error) {
+ if c.connected() {
return nil
}
c.connectionId = 0x41727101980
+ if c.socket == nil {
+ c.socket, err = net.Dial("udp", c.url.Host)
+ if err != nil {
+ return
+ }
+ }
b, err := c.request(Connect, nil)
if err != nil {
return
package udp_tracker
import (
+ "bitbucket.org/anacrolix/go.torrent/tracker"
"bytes"
+ "crypto/rand"
"encoding/binary"
+ "encoding/hex"
"io"
"net"
+ "net/url"
"syscall"
"testing"
)
t.FailNow()
}
}
+
+func TestUDPTracker(t *testing.T) {
+ tr, err := tracker.New(func() *url.URL {
+ u, err := url.Parse("udp://tracker.openbittorrent.com:80/announce")
+ if err != nil {
+ t.Fatal(err)
+ }
+ return u
+ }())
+ if err != nil {
+ t.Fatal(err)
+ }
+ if err := tr.Connect(); err != nil {
+ t.Fatal(err)
+ }
+ req := tracker.AnnounceRequest{
+ NumWant: -1,
+ Event: tracker.Started,
+ }
+ rand.Read(req.PeerId[:])
+ n, err := hex.Decode(req.InfoHash[:], []byte("c833bb2b5e7bcb9c07f4c020b4be430c28ba7cdb"))
+ if err != nil {
+ t.Fatal(err)
+ }
+ if n != len(req.InfoHash) {
+ panic("nope")
+ }
+ resp, err := tr.Announce(&req)
+ if err != nil {
+ t.Fatal(err)
+ }
+ t.Log(resp)
+}