diff --git a/backend/backend.go b/backend/backend.go index 3c06c18..ba40324 100644 --- a/backend/backend.go +++ b/backend/backend.go @@ -14,70 +14,109 @@ // 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. +// has been delivered to a BitTorrent client. package backend import ( + "log" "time" - "log" + "golang.org/x/net/context" "github.com/jzelinskie/trakr/bittorrent" "github.com/jzelinskie/trakr/frontend" - "golang.org/x/net/context" ) -// GenericConfig is a block of configuration who's structure is unknown. -type GenericConfig struct { - name string `yaml:"name"` - config interface{} `yaml:"config"` -} - type BackendConfig struct { - AnnounceInterval time.Duration `yaml:"announce_interval"` - PreHooks []GenericConfig `yaml:"prehooks"` - PostHooks []GenericConfig `yaml:"posthooks"` + AnnounceInterval time.Duration `yaml:"announce_interval"` } var _ frontend.TrackerFuncs = &Backend{} -func New(config BackendConfig, peerStore PeerStore) (*Backend, error) { - // Build TrackerFuncs from the PreHooks and PostHooks - return &Backend{peerStore: peerStore}, nil +func New(config BackendConfig, peerStore PeerStore, announcePreHooks, announcePostHooks, scrapePreHooks, scrapePostHooks []Hook) (*Backend, error) { + toReturn := &Backend{ + announceInterval: config.AnnounceInterval, + peerStore: peerStore, + announcePreHooks: announcePreHooks, + announcePostHooks: announcePostHooks, + scrapePreHooks: scrapePreHooks, + scrapePostHooks: scrapePostHooks, + } + + if len(toReturn.announcePreHooks) == 0 { + toReturn.announcePreHooks = []Hook{nopHook{}} + } + + if len(toReturn.announcePostHooks) == 0 { + toReturn.announcePostHooks = []Hook{nopHook{}} + } + + if len(toReturn.scrapePreHooks) == 0 { + toReturn.scrapePreHooks = []Hook{nopHook{}} + } + + if len(toReturn.scrapePostHooks) == 0 { + toReturn.scrapePostHooks = []Hook{nopHook{}} + } + + return toReturn, nil } -// Backend is a multi-protocol, customizable BitTorrent Tracker. +// Backend is a protocol-agnostic backend of a BitTorrent tracker. type Backend struct { - peerStore PeerStore - handleAnnounce func(context.Context, *bittorrent.AnnounceRequest) (*bittorrent.AnnounceResponse, error) - afterAnnounce func(context.Context, *bittorrent.AnnounceRequest, *bittorrent.AnnounceResponse) error - handleScrape func(context.Context, *bittorrent.ScrapeRequest) (*bittorrent.ScrapeResponse, error) - afterScrape func(context.Context, *bittorrent.ScrapeRequest, *bittorrent.ScrapeResponse) error + announceInterval time.Duration + peerStore PeerStore + announcePreHooks []Hook + announcePostHooks []Hook + scrapePreHooks []Hook + scrapePostHooks []Hook } // HandleAnnounce generates a response for an Announce. func (b *Backend) HandleAnnounce(ctx context.Context, req *bittorrent.AnnounceRequest) (*bittorrent.AnnounceResponse, error) { - return b.handleAnnounce(ctx, req) + resp := &bittorrent.AnnounceResponse{ + Interval: b.announceInterval, + } + for _, h := range b.announcePreHooks { + if err := h.HandleAnnounce(ctx, req, resp); err != nil { + return nil, err + } + } + + return resp, nil } // AfterAnnounce does something with the results of an Announce after it // has been completed. func (b *Backend) AfterAnnounce(ctx context.Context, req *bittorrent.AnnounceRequest, resp *bittorrent.AnnounceResponse) { - err := b.afterAnnounce(ctx, req, resp) - if err != nil { - log.Println("trakr: post-announce hooks failed:", err.Error()) + for _, h := range b.announcePostHooks { + if err := h.HandleAnnounce(ctx, req, resp); err != nil { + log.Println("trakr: post-announce hooks failed:", err.Error()) + return + } } } // HandleScrape generates a response for a Scrape. func (b *Backend) HandleScrape(ctx context.Context, req *bittorrent.ScrapeRequest) (*bittorrent.ScrapeResponse, error) { - return b.handleScrape(ctx, req) + resp := &bittorrent.ScrapeResponse{ + Files: make(map[bittorrent.InfoHash]bittorrent.Scrape), + } + for _, h := range b.scrapePreHooks { + if err := h.HandleScrape(ctx, req, resp); err != nil { + return nil, err + } + } + + return resp, nil } // AfterScrape does something with the results of a Scrape after it has been completed. func (b *Backend) AfterScrape(ctx context.Context, req *bittorrent.ScrapeRequest, resp *bittorrent.ScrapeResponse) { - err := b.afterScrape(ctx, req, resp) - if err != nil { - log.Println("trakr: post-scrape hooks failed:", err.Error()) + for _, h := range b.scrapePostHooks { + if err := h.HandleScrape(ctx, req, resp); err != nil { + log.Println("trakr: post-scrape hooks failed:", err.Error()) + return + } } } diff --git a/backend/hooks.go b/backend/hooks.go index 484c4b9..2c1792a 100644 --- a/backend/hooks.go +++ b/backend/hooks.go @@ -15,8 +15,6 @@ package backend import ( - "fmt" - "golang.org/x/net/context" "github.com/jzelinskie/trakr/bittorrent" @@ -29,55 +27,12 @@ type Hook interface { HandleScrape(context.Context, *bittorrent.ScrapeRequest, *bittorrent.ScrapeResponse) error } -// HookConstructor is a function used to create a new instance of a Hook. -type HookConstructor func(interface{}) (Hook, error) +type nopHook struct{} -var preHooks = make(map[string]HookConstructor) - -// RegisterPreHook makes a HookConstructor available by the provided name. -// -// If this function is called twice with the same name or if the -// HookConstructor is nil, it panics. -func RegisterPreHook(name string, con HookConstructor) { - if con == nil { - panic("trakr: could not register nil HookConstructor") - } - if _, dup := preHooks[name]; dup { - panic("trakr: could not register duplicate HookConstructor: " + name) - } - preHooks[name] = con +func (nopHook) HandleAnnounce(context.Context, *bittorrent.AnnounceRequest, *bittorrent.AnnounceResponse) error { + return nil } -// NewPreHook creates an instance of the given PreHook by name. -func NewPreHook(name string, config interface{}) (Hook, error) { - con, ok := preHooks[name] - if !ok { - return nil, fmt.Errorf("trakr: unknown PreHook %q (forgotten import?)", name) - } - return con(config) -} - -var postHooks = make(map[string]HookConstructor) - -// RegisterPostHook makes a HookConstructor available by the provided name. -// -// If this function is called twice with the same name or if the -// HookConstructor is nil, it panics. -func RegisterPostHook(name string, con HookConstructor) { - if con == nil { - panic("trakr: could not register nil HookConstructor") - } - if _, dup := postHooks[name]; dup { - panic("trakr: could not register duplicate HookConstructor: " + name) - } - preHooks[name] = con -} - -// NewPostHook creates an instance of the given PostHook by name. -func NewPostHook(name string, config interface{}) (Hook, error) { - con, ok := preHooks[name] - if !ok { - return nil, fmt.Errorf("trakr: unknown PostHook %q (forgotten import?)", name) - } - return con(config) +func (nopHook) HandleScrape(context.Context, *bittorrent.ScrapeRequest, *bittorrent.ScrapeResponse) error { + return nil } diff --git a/cmd/trakr/main.go b/cmd/trakr/main.go index d8cd4fc..9e7f9a2 100644 --- a/cmd/trakr/main.go +++ b/cmd/trakr/main.go @@ -15,7 +15,6 @@ import ( "gopkg.in/yaml.v2" "github.com/jzelinskie/trakr/backend" - httpfrontend "github.com/jzelinskie/trakr/frontend/http" udpfrontend "github.com/jzelinskie/trakr/frontend/udp" ) @@ -95,7 +94,8 @@ func main() { }() // TODO create PeerStore - trackerBackend, err := backend.New(configFile.Config.BackendConfig, nil) + // TODO create Hooks + trackerBackend, err := backend.New(configFile.Config.BackendConfig, nil, nil, nil, nil, nil) if err != nil { return err } @@ -108,7 +108,7 @@ func main() { if configFile.Config.HTTPConfig.Addr != "" { // TODO get the real TrackerFuncs - hFrontend = httpfrontend.NewFrontend(trackerBackend.TrackerFuncs, configFile.Config.HTTPConfig) + hFrontend = httpfrontend.NewFrontend(trackerBackend, configFile.Config.HTTPConfig) go func() { log.Println("started serving HTTP on", configFile.Config.HTTPConfig.Addr) @@ -120,7 +120,7 @@ func main() { if configFile.Config.UDPConfig.Addr != "" { // TODO get the real TrackerFuncs - uFrontend = udpfrontend.NewFrontend(trackerBackend.TrackerFuncs, configFile.Config.UDPConfig) + uFrontend = udpfrontend.NewFrontend(trackerBackend, configFile.Config.UDPConfig) go func() { log.Println("started serving UDP on", configFile.Config.UDPConfig.Addr)