restructure into backend and frontends
This commit is contained in:
parent
11d135ce49
commit
8f67c1018e
22 changed files with 132 additions and 83 deletions
|
@ -15,13 +15,13 @@
|
|||
// Package trakr implements a BitTorrent Tracker that supports multiple
|
||||
// protocols and configurable Hooks that execute before and after a Response
|
||||
// has been delievered to a BitTorrent client.
|
||||
package trakr
|
||||
package backend
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
"github.com/jzelinskie/trakr/bittorrent/http"
|
||||
"github.com/jzelinskie/trakr/bittorrent/udp"
|
||||
"github.com/jzelinskie/trakr/bittorrent"
|
||||
"golang.org/x/net/context"
|
||||
)
|
||||
|
||||
// GenericConfig is a block of configuration who's structure is unknown.
|
||||
|
@ -30,38 +30,32 @@ type GenericConfig struct {
|
|||
config interface{} `yaml:"config"`
|
||||
}
|
||||
|
||||
// MultiTracker is a multi-protocol, customizable BitTorrent Tracker.
|
||||
type MultiTracker struct {
|
||||
// Backend is a multi-protocol, customizable BitTorrent Tracker.
|
||||
type Backend struct {
|
||||
AnnounceInterval time.Duration `yaml:"announce_interval"`
|
||||
GCInterval time.Duration `yaml:"gc_interval"`
|
||||
GCExpiration time.Duration `yaml:"gc_expiration"`
|
||||
HTTPConfig http.Config `yaml:"http"`
|
||||
UDPConfig udp.Config `yaml:"udp"`
|
||||
PeerStoreConfig []GenericConfig `yaml:"storage"`
|
||||
PreHooks []GenericConfig `yaml:"prehooks"`
|
||||
PostHooks []GenericConfig `yaml:"posthooks"`
|
||||
|
||||
peerStore PeerStore
|
||||
httpTracker http.Tracker
|
||||
udpTracker udp.Tracker
|
||||
closing chan struct{}
|
||||
peerStore PeerStore
|
||||
closing chan struct{}
|
||||
}
|
||||
|
||||
// Stop provides a thread-safe way to shutdown a currently running
|
||||
// MultiTracker.
|
||||
func (t *MultiTracker) Stop() {
|
||||
// Backend.
|
||||
func (t *Backend) Stop() {
|
||||
close(t.closing)
|
||||
}
|
||||
|
||||
// ListenAndServe listens on the protocols and addresses specified in the
|
||||
// HTTPConfig and UDPConfig then blocks serving BitTorrent requests until
|
||||
// t.Stop() is called or an error is returned.
|
||||
func (t *MultiTracker) ListenAndServe() error {
|
||||
// Start starts the Backend.
|
||||
// It blocks until t.Stop() is called or an error is returned.
|
||||
func (t *Backend) Start() error {
|
||||
t.closing = make(chan struct{})
|
||||
// Build an TrackerFuncs from the PreHooks and PostHooks.
|
||||
// Create a PeerStore instance.
|
||||
// Create a HTTP Tracker instance.
|
||||
// Create a UDP Tracker instance.
|
||||
// Make TrackerFuncs available to be used by frontends.
|
||||
select {
|
||||
case <-t.closing:
|
||||
return nil
|
||||
|
@ -69,3 +63,27 @@ func (t *MultiTracker) ListenAndServe() error {
|
|||
|
||||
return nil
|
||||
}
|
||||
|
||||
// TrackerFuncs is the collection of callback functions provided by the Backend
|
||||
// to (1) generate a response from a parsed request, and (2) observe anything
|
||||
// after the response has been delivered to the client.
|
||||
type TrackerFuncs struct {
|
||||
HandleAnnounce AnnounceHandler
|
||||
HandleScrape ScrapeHandler
|
||||
AfterAnnounce AnnounceCallback
|
||||
AfterScrape ScrapeCallback
|
||||
}
|
||||
|
||||
// AnnounceHandler is a function that generates a response for an Announce.
|
||||
type AnnounceHandler func(context.Context, *bittorrent.AnnounceRequest) (*bittorrent.AnnounceResponse, error)
|
||||
|
||||
// AnnounceCallback is a function that does something with the results of an
|
||||
// Announce after it has been completed.
|
||||
type AnnounceCallback func(*bittorrent.AnnounceRequest, *bittorrent.AnnounceResponse)
|
||||
|
||||
// ScrapeHandler is a function that generates a response for a Scrape.
|
||||
type ScrapeHandler func(context.Context, *bittorrent.ScrapeRequest) (*bittorrent.ScrapeResponse, error)
|
||||
|
||||
// ScrapeCallback is a function that does something with the results of a
|
||||
// Scrape after it has been completed.
|
||||
type ScrapeCallback func(*bittorrent.ScrapeRequest, *bittorrent.ScrapeResponse)
|
|
@ -12,7 +12,7 @@
|
|||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package trakr
|
||||
package backend
|
||||
|
||||
import (
|
||||
"fmt"
|
|
@ -1,4 +1,4 @@
|
|||
package trakr
|
||||
package backend
|
||||
|
||||
import (
|
||||
"fmt"
|
|
@ -20,8 +20,6 @@ package bittorrent
|
|||
import (
|
||||
"net"
|
||||
"time"
|
||||
|
||||
"golang.org/x/net/context"
|
||||
)
|
||||
|
||||
// PeerID represents a peer ID.
|
||||
|
@ -108,13 +106,6 @@ type AnnounceResponse struct {
|
|||
IPv6Peers []Peer
|
||||
}
|
||||
|
||||
// AnnounceHandler is a function that generates a response for an Announce.
|
||||
type AnnounceHandler func(context.Context, *AnnounceRequest) (*AnnounceResponse, error)
|
||||
|
||||
// AnnounceCallback is a function that does something with the results of an
|
||||
// Announce after it has been completed.
|
||||
type AnnounceCallback func(*AnnounceRequest, *AnnounceResponse)
|
||||
|
||||
// ScrapeRequest represents the parsed parameters from a scrape request.
|
||||
type ScrapeRequest struct {
|
||||
InfoHashes []InfoHash
|
||||
|
@ -133,13 +124,6 @@ type Scrape struct {
|
|||
Incomplete uint32
|
||||
}
|
||||
|
||||
// ScrapeHandler is a function that generates a response for a Scrape.
|
||||
type ScrapeHandler func(context.Context, *ScrapeRequest) (*ScrapeResponse, error)
|
||||
|
||||
// ScrapeCallback is a function that does something with the results of a
|
||||
// Scrape after it has been completed.
|
||||
type ScrapeCallback func(*ScrapeRequest, *ScrapeResponse)
|
||||
|
||||
// Peer represents the connection details of a peer that is returned in an
|
||||
// announce response.
|
||||
type Peer struct {
|
||||
|
@ -171,13 +155,3 @@ type Tracker interface {
|
|||
ListenAndServe() error
|
||||
Stop()
|
||||
}
|
||||
|
||||
// TrackerFuncs is the collection of callback functions provided to a Tracker
|
||||
// to (1) generate a response from a parsed request, and (2) observe anything
|
||||
// after the response has been delivered to the client.
|
||||
type TrackerFuncs struct {
|
||||
HandleAnnounce AnnounceHandler
|
||||
HandleScrape ScrapeHandler
|
||||
AfterAnnounce AnnounceCallback
|
||||
AfterScrape ScrapeCallback
|
||||
}
|
||||
|
|
|
@ -14,13 +14,18 @@ import (
|
|||
"github.com/spf13/cobra"
|
||||
"gopkg.in/yaml.v2"
|
||||
|
||||
"github.com/jzelinskie/trakr"
|
||||
"github.com/jzelinskie/trakr/backend"
|
||||
|
||||
httpfrontend "github.com/jzelinskie/trakr/frontends/http"
|
||||
udpfrontend "github.com/jzelinskie/trakr/frontends/udp"
|
||||
)
|
||||
|
||||
type ConfigFile struct {
|
||||
Config struct {
|
||||
PrometheusAddr string `yaml:"prometheus_addr"`
|
||||
trakr.MultiTracker
|
||||
backend.Backend
|
||||
HTTPConfig httpfrontend.Config `yaml:"http"`
|
||||
UDPConfig udpfrontend.Config `yaml:"udp"`
|
||||
} `yaml:"trakr"`
|
||||
}
|
||||
|
||||
|
@ -89,15 +94,66 @@ func main() {
|
|||
}
|
||||
}()
|
||||
|
||||
errChan := make(chan error)
|
||||
closedChan := make(chan struct{})
|
||||
|
||||
go func() {
|
||||
shutdown := make(chan os.Signal)
|
||||
signal.Notify(shutdown, syscall.SIGINT, syscall.SIGTERM)
|
||||
<-shutdown
|
||||
configFile.Config.MultiTracker.Stop()
|
||||
if err := configFile.Config.Backend.Start(); err != nil {
|
||||
errChan <- errors.New("failed to cleanly shutdown: " + err.Error())
|
||||
}
|
||||
}()
|
||||
|
||||
if err := configFile.Config.MultiTracker.ListenAndServe(); err != nil {
|
||||
return errors.New("failed to cleanly shutdown: " + err.Error())
|
||||
var hFrontend *httpfrontend.Frontend
|
||||
var uFrontend *udpfrontend.Frontend
|
||||
|
||||
if configFile.Config.HTTPConfig.Addr != "" {
|
||||
// TODO get the real TrackerFuncs
|
||||
hFrontend = httpfrontend.NewFrontend(backend.TrackerFuncs{}, configFile.Config.HTTPConfig)
|
||||
|
||||
go func() {
|
||||
log.Println("started serving HTTP on", configFile.Config.HTTPConfig.Addr)
|
||||
if err := hFrontend.ListenAndServe(); err != nil {
|
||||
errChan <- errors.New("failed to cleanly shutdown HTTP frontend: " + err.Error())
|
||||
}
|
||||
}()
|
||||
}
|
||||
|
||||
if configFile.Config.UDPConfig.Addr != "" {
|
||||
// TODO get the real TrackerFuncs
|
||||
uFrontend = udpfrontend.NewFrontend(backend.TrackerFuncs{}, configFile.Config.UDPConfig)
|
||||
|
||||
go func() {
|
||||
log.Println("started serving UDP on", configFile.Config.UDPConfig.Addr)
|
||||
if err := uFrontend.ListenAndServe(); err != nil {
|
||||
errChan <- errors.New("failed to cleanly shutdown UDP frontend: " + err.Error())
|
||||
}
|
||||
}()
|
||||
}
|
||||
|
||||
shutdown := make(chan os.Signal)
|
||||
signal.Notify(shutdown, syscall.SIGINT, syscall.SIGTERM)
|
||||
go func() {
|
||||
<-shutdown
|
||||
|
||||
if uFrontend != nil {
|
||||
uFrontend.Stop()
|
||||
}
|
||||
|
||||
if hFrontend != nil {
|
||||
hFrontend.Stop()
|
||||
}
|
||||
|
||||
configFile.Config.Backend.Stop()
|
||||
|
||||
close(errChan)
|
||||
close(closedChan)
|
||||
}()
|
||||
|
||||
err = <-errChan
|
||||
if err != nil {
|
||||
close(shutdown)
|
||||
<-closedChan
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
|
|
|
@ -12,7 +12,7 @@
|
|||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
// Package http implements a BitTorrent tracker via the HTTP protocol as
|
||||
// Package http implements a BitTorrent frontend via the HTTP protocol as
|
||||
// described in BEP 3 and BEP 23.
|
||||
package http
|
||||
|
||||
|
@ -26,7 +26,7 @@ import (
|
|||
"github.com/tylerb/graceful"
|
||||
"golang.org/x/net/context"
|
||||
|
||||
"github.com/jzelinskie/trakr/bittorrent"
|
||||
"github.com/jzelinskie/trakr/backend"
|
||||
)
|
||||
|
||||
func init() {
|
||||
|
@ -43,8 +43,8 @@ var promResponseDurationMilliseconds = prometheus.NewHistogramVec(
|
|||
[]string{"action", "error"},
|
||||
)
|
||||
|
||||
// recordResponseDuration records the duration of time to respond to a UDP
|
||||
// Request in milliseconds .
|
||||
// recordResponseDuration records the duration of time to respond to a Request
|
||||
// in milliseconds .
|
||||
func recordResponseDuration(action string, err error, duration time.Duration) {
|
||||
var errString string
|
||||
if err != nil {
|
||||
|
@ -57,7 +57,7 @@ func recordResponseDuration(action string, err error, duration time.Duration) {
|
|||
}
|
||||
|
||||
// Config represents all of the configurable options for an HTTP BitTorrent
|
||||
// Tracker.
|
||||
// Frontend.
|
||||
type Config struct {
|
||||
Addr string
|
||||
ReadTimeout time.Duration
|
||||
|
@ -67,29 +67,29 @@ type Config struct {
|
|||
RealIPHeader string
|
||||
}
|
||||
|
||||
// Tracker holds the state of an HTTP BitTorrent Tracker.
|
||||
type Tracker struct {
|
||||
// Frontend holds the state of an HTTP BitTorrent Frontend.
|
||||
type Frontend struct {
|
||||
grace *graceful.Server
|
||||
|
||||
bittorrent.TrackerFuncs
|
||||
backend.TrackerFuncs
|
||||
Config
|
||||
}
|
||||
|
||||
// NewTracker allocates a new instance of a Tracker.
|
||||
func NewTracker(funcs bittorrent.TrackerFuncs, cfg Config) *Tracker {
|
||||
return &Tracker{
|
||||
// NewFrontend allocates a new instance of a Frontend.
|
||||
func NewFrontend(funcs backend.TrackerFuncs, cfg Config) *Frontend {
|
||||
return &Frontend{
|
||||
TrackerFuncs: funcs,
|
||||
Config: cfg,
|
||||
}
|
||||
}
|
||||
|
||||
// Stop provides a thread-safe way to shutdown a currently running Tracker.
|
||||
func (t *Tracker) Stop() {
|
||||
func (t *Frontend) Stop() {
|
||||
t.grace.Stop(t.grace.Timeout)
|
||||
<-t.grace.StopChan()
|
||||
}
|
||||
|
||||
func (t *Tracker) handler() http.Handler {
|
||||
func (t *Frontend) handler() http.Handler {
|
||||
router := httprouter.New()
|
||||
router.GET("/announce", t.announceRoute)
|
||||
router.GET("/scrape", t.scrapeRoute)
|
||||
|
@ -98,7 +98,7 @@ func (t *Tracker) handler() http.Handler {
|
|||
|
||||
// ListenAndServe listens on the TCP network address t.Addr and blocks serving
|
||||
// BitTorrent requests until t.Stop() is called or an error is returned.
|
||||
func (t *Tracker) ListenAndServe() error {
|
||||
func (t *Frontend) ListenAndServe() error {
|
||||
t.grace = &graceful.Server{
|
||||
Server: &http.Server{
|
||||
Addr: t.Addr,
|
||||
|
@ -139,7 +139,7 @@ func (t *Tracker) ListenAndServe() error {
|
|||
}
|
||||
|
||||
// announceRoute parses and responds to an Announce by using t.TrackerFuncs.
|
||||
func (t *Tracker) announceRoute(w http.ResponseWriter, r *http.Request, _ httprouter.Params) {
|
||||
func (t *Frontend) announceRoute(w http.ResponseWriter, r *http.Request, _ httprouter.Params) {
|
||||
var err error
|
||||
start := time.Now()
|
||||
defer recordResponseDuration("announce", err, time.Since(start))
|
||||
|
@ -168,7 +168,7 @@ func (t *Tracker) announceRoute(w http.ResponseWriter, r *http.Request, _ httpro
|
|||
}
|
||||
|
||||
// scrapeRoute parses and responds to a Scrape by using t.TrackerFuncs.
|
||||
func (t *Tracker) scrapeRoute(w http.ResponseWriter, r *http.Request, _ httprouter.Params) {
|
||||
func (t *Frontend) scrapeRoute(w http.ResponseWriter, r *http.Request, _ httprouter.Params) {
|
||||
var err error
|
||||
start := time.Now()
|
||||
defer recordResponseDuration("scrape", err, time.Since(start))
|
|
@ -18,7 +18,7 @@ import (
|
|||
"net/http"
|
||||
|
||||
"github.com/jzelinskie/trakr/bittorrent"
|
||||
"github.com/jzelinskie/trakr/bittorrent/http/bencode"
|
||||
"github.com/jzelinskie/trakr/frontends/http/bencode"
|
||||
)
|
||||
|
||||
// WriteError communicates an error to a BitTorrent client over HTTP.
|
|
@ -27,8 +27,9 @@ import (
|
|||
"github.com/prometheus/client_golang/prometheus"
|
||||
"golang.org/x/net/context"
|
||||
|
||||
"github.com/jzelinskie/trakr/backend"
|
||||
"github.com/jzelinskie/trakr/bittorrent"
|
||||
"github.com/jzelinskie/trakr/bittorrent/udp/bytepool"
|
||||
"github.com/jzelinskie/trakr/frontends/udp/bytepool"
|
||||
)
|
||||
|
||||
func init() {
|
||||
|
@ -67,27 +68,27 @@ type Config struct {
|
|||
AllowIPSpoofing bool
|
||||
}
|
||||
|
||||
// Tracker holds the state of a UDP BitTorrent Tracker.
|
||||
type Tracker struct {
|
||||
// Frontend holds the state of a UDP BitTorrent Frontend.
|
||||
type Frontend struct {
|
||||
socket *net.UDPConn
|
||||
closing chan struct{}
|
||||
wg sync.WaitGroup
|
||||
|
||||
bittorrent.TrackerFuncs
|
||||
backend.TrackerFuncs
|
||||
Config
|
||||
}
|
||||
|
||||
// NewTracker allocates a new instance of a Tracker.
|
||||
func NewTracker(funcs bittorrent.TrackerFuncs, cfg Config) *Tracker {
|
||||
return &Tracker{
|
||||
// NewFrontend allocates a new instance of a Frontend.
|
||||
func NewFrontend(funcs backend.TrackerFuncs, cfg Config) *Frontend {
|
||||
return &Frontend{
|
||||
closing: make(chan struct{}),
|
||||
TrackerFuncs: funcs,
|
||||
Config: cfg,
|
||||
}
|
||||
}
|
||||
|
||||
// Stop provides a thread-safe way to shutdown a currently running Tracker.
|
||||
func (t *Tracker) Stop() {
|
||||
// Stop provides a thread-safe way to shutdown a currently running Frontend.
|
||||
func (t *Frontend) Stop() {
|
||||
close(t.closing)
|
||||
t.socket.SetReadDeadline(time.Now())
|
||||
t.wg.Wait()
|
||||
|
@ -95,7 +96,7 @@ func (t *Tracker) Stop() {
|
|||
|
||||
// ListenAndServe listens on the UDP network address t.Addr and blocks serving
|
||||
// BitTorrent requests until t.Stop() is called or an error is returned.
|
||||
func (t *Tracker) ListenAndServe() error {
|
||||
func (t *Frontend) ListenAndServe() error {
|
||||
udpAddr, err := net.ResolveUDPAddr("udp", t.Addr)
|
||||
if err != nil {
|
||||
return err
|
||||
|
@ -175,7 +176,7 @@ func (w ResponseWriter) Write(b []byte) (int, error) {
|
|||
}
|
||||
|
||||
// handleRequest parses and responds to a UDP Request.
|
||||
func (t *Tracker) handleRequest(r Request, w ResponseWriter) (response []byte, actionName string, err error) {
|
||||
func (t *Frontend) handleRequest(r Request, w ResponseWriter) (response []byte, actionName string, err error) {
|
||||
if len(r.Packet) < 16 {
|
||||
// Malformed, no client packets are less than 16 bytes.
|
||||
// We explicitly return nothing in case this is a DoS attempt.
|
Loading…
Add table
Reference in a new issue