24 optionTypeEndOfOptions = 0
29 type ConnectionRequest struct {
35 type ConnectionResponse struct {
39 type ResponseHeader struct {
44 type RequestHeader struct {
50 type AnnounceResponseHeader struct {
57 RegisterClientScheme("udp", newClient)
60 func newClient(url *url.URL) Client {
66 func newTransactionId() int32 {
67 return int32(rand.Uint32())
70 func timeout(contiguousTimeouts int) (d time.Duration) {
71 if contiguousTimeouts > 8 {
72 contiguousTimeouts = 8
75 for ; contiguousTimeouts > 0; contiguousTimeouts-- {
81 type udpClient struct {
82 contiguousTimeouts int
83 connectionIdReceived time.Time
89 func (c *udpClient) URL() string {
93 func (c *udpClient) String() string {
97 func (c *udpClient) Announce(req *AnnounceRequest) (res AnnounceResponse, err error) {
102 reqURI := c.url.RequestURI()
103 // Clearly this limits the request URI to 255 bytes. BEP 41 supports
104 // longer but I'm not fussed.
105 options := append([]byte{optionTypeURLData, byte(len(reqURI))}, []byte(reqURI)...)
106 b, err := c.request(Announce, req, options)
110 var h AnnounceResponseHeader
111 err = readBody(b, &h)
114 err = io.ErrUnexpectedEOF
116 err = fmt.Errorf("error parsing announce response: %s", err)
119 res.Interval = h.Interval
120 res.Leechers = h.Leechers
121 res.Seeders = h.Seeders
124 err = binary.Read(b, binary.BigEndian, &p)
133 res.Peers = append(res.Peers, Peer{
140 // body is the binary serializable request body. trailer is optional data
141 // following it, such as for BEP 41.
142 func (c *udpClient) write(h *RequestHeader, body interface{}, trailer []byte) (err error) {
143 buf := &bytes.Buffer{}
144 err = binary.Write(buf, binary.BigEndian, h)
149 err = binary.Write(buf, binary.BigEndian, body)
154 _, err = buf.Write(trailer)
158 n, err := c.socket.Write(buf.Bytes())
163 panic("write should send all or error")
168 func read(r io.Reader, data interface{}) error {
169 return binary.Read(r, binary.BigEndian, data)
172 func write(w io.Writer, data interface{}) error {
173 return binary.Write(w, binary.BigEndian, data)
176 // args is the binary serializable request body. trailer is optional data
177 // following it, such as for BEP 41.
178 func (c *udpClient) request(action Action, args interface{}, options []byte) (responseBody *bytes.Reader, err error) {
179 tid := newTransactionId()
180 err = c.write(&RequestHeader{
181 ConnectionId: c.connectionId,
188 c.socket.SetReadDeadline(time.Now().Add(timeout(c.contiguousTimeouts)))
189 b := make([]byte, 0x10000) // IP limits packet size to 64KB
192 n, err = c.socket.Read(b)
193 if opE, ok := err.(*net.OpError); ok {
195 c.contiguousTimeouts++
202 buf := bytes.NewBuffer(b[:n])
204 err = binary.Read(buf, binary.BigEndian, &h)
206 case io.ErrUnexpectedEOF:
212 if h.TransactionId != tid {
215 c.contiguousTimeouts = 0
216 if h.Action == Error {
217 err = errors.New(buf.String())
219 responseBody = bytes.NewReader(buf.Bytes())
224 func readBody(r *bytes.Reader, data ...interface{}) (err error) {
225 for _, datum := range data {
226 err = binary.Read(r, binary.BigEndian, datum)
234 func (c *udpClient) connected() bool {
235 return !c.connectionIdReceived.IsZero() && time.Now().Before(c.connectionIdReceived.Add(time.Minute))
238 func (c *udpClient) Connect() (err error) {
242 c.connectionId = 0x41727101980
244 c.socket, err = net.Dial("udp", c.url.Host)
249 b, err := c.request(Connect, nil, nil)
253 var res ConnectionResponse
254 err = readBody(b, &res)
258 c.connectionId = res.ConnectionId
259 c.connectionIdReceived = time.Now()