]> Sergey Matveev's repositories - btrtrc.git/blob - tracker/http/server/server.go
Support HTTP tracker server
[btrtrc.git] / tracker / http / server / server.go
1 package httpTrackerServer
2
3 import (
4         "fmt"
5         "net"
6         "net/http"
7         "net/netip"
8         "net/url"
9
10         "github.com/anacrolix/dht/v2/krpc"
11         "github.com/anacrolix/log"
12
13         "github.com/anacrolix/torrent/bencode"
14         "github.com/anacrolix/torrent/tracker"
15         httpTracker "github.com/anacrolix/torrent/tracker/http"
16         udpTrackerServer "github.com/anacrolix/torrent/tracker/udp/server"
17 )
18
19 type Handler struct {
20         AnnounceTracker udpTrackerServer.AnnounceTracker
21 }
22
23 func unmarshalQueryKeyToArray(w http.ResponseWriter, key string, query url.Values) (ret [20]byte, ok bool) {
24         str := query.Get(key)
25         if len(str) != len(ret) {
26                 http.Error(w, fmt.Sprintf("%v has wrong length", key), http.StatusBadRequest)
27                 return
28         }
29         copy(ret[:], str)
30         ok = true
31         return
32 }
33
34 func (me Handler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
35         vs := r.URL.Query()
36         var event tracker.AnnounceEvent
37         err := event.UnmarshalText([]byte(vs.Get("event")))
38         if err != nil {
39                 http.Error(w, err.Error(), http.StatusBadRequest)
40                 return
41         }
42         infoHash, ok := unmarshalQueryKeyToArray(w, "info_hash", vs)
43         if !ok {
44                 return
45         }
46         peerId, ok := unmarshalQueryKeyToArray(w, "peer_id", vs)
47         if !ok {
48                 return
49         }
50         host, _, err := net.SplitHostPort(r.RemoteAddr)
51         if err != nil {
52                 log.Printf("error splitting remote port: %v", err)
53                 http.Error(w, "error determining your IP", http.StatusInternalServerError)
54                 return
55         }
56         addrPort, err := netip.ParseAddrPort(net.JoinHostPort(host, vs.Get("port")))
57         err = me.AnnounceTracker.TrackAnnounce(r.Context(), tracker.AnnounceRequest{
58                 InfoHash: infoHash,
59                 PeerId:   peerId,
60                 Event:    event,
61                 Port:     addrPort.Port(),
62         }, addrPort)
63         if err != nil {
64                 log.Printf("error tracking announce: %v", err)
65                 http.Error(w, "error tracking announce", http.StatusInternalServerError)
66                 return
67         }
68         peers, err := me.AnnounceTracker.GetPeers(r.Context(), infoHash, tracker.GetPeersOpts{})
69         if err != nil {
70                 log.Printf("error getting peers: %v", err)
71                 http.Error(w, "error getting peers", http.StatusInternalServerError)
72                 return
73         }
74         var resp httpTracker.HttpResponse
75         resp.Interval = 5 * 60
76         resp.Peers.Compact = true
77         for _, peer := range peers {
78                 if peer.Addr().Is4() {
79                         resp.Peers.List = append(resp.Peers.List, tracker.Peer{
80                                 IP:   peer.Addr().AsSlice(),
81                                 Port: int(peer.Port()),
82                         })
83                 } else if peer.Addr().Is6() {
84                         resp.Peers6 = append(resp.Peers6, krpc.NodeAddr{
85                                 IP:   peer.Addr().AsSlice(),
86                                 Port: int(peer.Port()),
87                         })
88                 }
89         }
90         err = bencode.NewEncoder(w).Encode(resp)
91         if err != nil {
92                 log.Printf("error encoding and writing response body: %v", err)
93         }
94 }