diff --git a/backend/backend.go b/backend/backend.go index 1d97b02..3c06c18 100644 --- a/backend/backend.go +++ b/backend/backend.go @@ -20,7 +20,11 @@ package backend import ( "time" + "log" + + "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. @@ -35,6 +39,8 @@ type BackendConfig struct { PostHooks []GenericConfig `yaml:"posthooks"` } +var _ frontend.TrackerFuncs = &Backend{} + func New(config BackendConfig, peerStore PeerStore) (*Backend, error) { // Build TrackerFuncs from the PreHooks and PostHooks return &Backend{peerStore: peerStore}, nil @@ -42,6 +48,36 @@ func New(config BackendConfig, peerStore PeerStore) (*Backend, error) { // Backend is a multi-protocol, customizable BitTorrent Tracker. type Backend struct { - TrackerFuncs frontend.TrackerFuncs - peerStore PeerStore + 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 +} + +// 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) +} + +// 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()) + } +} + +// 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) +} + +// 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()) + } } diff --git a/frontend/frontend.go b/frontend/frontend.go index f445d99..89fa128 100644 --- a/frontend/frontend.go +++ b/frontend/frontend.go @@ -1,30 +1,25 @@ package frontend import ( - "github.com/jzelinskie/trakr/bittorrent" "golang.org/x/net/context" + + "github.com/jzelinskie/trakr/bittorrent" ) // 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 +type TrackerFuncs interface { + // HandleAnnounce generates a response for an Announce. + HandleAnnounce(context.Context, *bittorrent.AnnounceRequest) (*bittorrent.AnnounceResponse, error) + + // AfterAnnounce does something with the results of an Announce after it + // has been completed. + AfterAnnounce(context.Context, *bittorrent.AnnounceRequest, *bittorrent.AnnounceResponse) + + // HandleScrape generates a response for a Scrape. + HandleScrape(context.Context, *bittorrent.ScrapeRequest) (*bittorrent.ScrapeResponse, error) + + // AfterScrape does something with the results of a Scrape after it has been completed. + AfterScrape(context.Context, *bittorrent.ScrapeRequest, *bittorrent.ScrapeResponse) } - -// 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) diff --git a/frontend/http/frontend.go b/frontend/http/frontend.go index 4e3e5d7..991e064 100644 --- a/frontend/http/frontend.go +++ b/frontend/http/frontend.go @@ -71,15 +71,15 @@ type Config struct { type Frontend struct { grace *graceful.Server - frontend.TrackerFuncs + backend frontend.TrackerFuncs Config } // NewFrontend allocates a new instance of a Frontend. -func NewFrontend(funcs frontend.TrackerFuncs, cfg Config) *Frontend { +func NewFrontend(backend frontend.TrackerFuncs, cfg Config) *Frontend { return &Frontend{ - TrackerFuncs: funcs, - Config: cfg, + backend: backend, + Config: cfg, } } @@ -150,7 +150,7 @@ func (t *Frontend) announceRoute(w http.ResponseWriter, r *http.Request, _ httpr return } - resp, err := t.HandleAnnounce(context.TODO(), req) + resp, err := t.backend.HandleAnnounce(context.TODO(), req) if err != nil { WriteError(w, err) return @@ -162,9 +162,7 @@ func (t *Frontend) announceRoute(w http.ResponseWriter, r *http.Request, _ httpr return } - if t.AfterAnnounce != nil { - go t.AfterAnnounce(req, resp) - } + go t.backend.AfterAnnounce(context.TODO(), req, resp) } // scrapeRoute parses and responds to a Scrape by using t.TrackerFuncs. @@ -179,7 +177,7 @@ func (t *Frontend) scrapeRoute(w http.ResponseWriter, r *http.Request, _ httprou return } - resp, err := t.HandleScrape(context.TODO(), req) + resp, err := t.backend.HandleScrape(context.TODO(), req) if err != nil { WriteError(w, err) return @@ -191,7 +189,5 @@ func (t *Frontend) scrapeRoute(w http.ResponseWriter, r *http.Request, _ httprou return } - if t.AfterScrape != nil { - go t.AfterScrape(req, resp) - } + go t.backend.AfterScrape(context.TODO(), req, resp) } diff --git a/frontend/udp/frontend.go b/frontend/udp/frontend.go index f916e3c..70b4b19 100644 --- a/frontend/udp/frontend.go +++ b/frontend/udp/frontend.go @@ -74,16 +74,16 @@ type Frontend struct { closing chan struct{} wg sync.WaitGroup - frontend.TrackerFuncs + backend frontend.TrackerFuncs Config } // NewFrontend allocates a new instance of a Frontend. -func NewFrontend(funcs frontend.TrackerFuncs, cfg Config) *Frontend { +func NewFrontend(backend frontend.TrackerFuncs, cfg Config) *Frontend { return &Frontend{ - closing: make(chan struct{}), - TrackerFuncs: funcs, - Config: cfg, + closing: make(chan struct{}), + backend: backend, + Config: cfg, } } @@ -221,7 +221,7 @@ func (t *Frontend) handleRequest(r Request, w ResponseWriter) (response []byte, } var resp *bittorrent.AnnounceResponse - resp, err = t.HandleAnnounce(context.TODO(), req) + resp, err = t.backend.HandleAnnounce(context.TODO(), req) if err != nil { WriteError(w, txID, err) return @@ -229,9 +229,8 @@ func (t *Frontend) handleRequest(r Request, w ResponseWriter) (response []byte, WriteAnnounce(w, txID, resp) - if t.AfterAnnounce != nil { - go t.AfterAnnounce(req, resp) - } + // TODO(mrd0ll4r): evaluate if it's worth spawning another goroutine. + go t.backend.AfterAnnounce(context.TODO(), req, resp) return @@ -246,7 +245,7 @@ func (t *Frontend) handleRequest(r Request, w ResponseWriter) (response []byte, } var resp *bittorrent.ScrapeResponse - resp, err = t.HandleScrape(context.TODO(), req) + resp, err = t.backend.HandleScrape(context.TODO(), req) if err != nil { WriteError(w, txID, err) return @@ -254,9 +253,7 @@ func (t *Frontend) handleRequest(r Request, w ResponseWriter) (response []byte, WriteScrape(w, txID, resp) - if t.AfterScrape != nil { - go t.AfterScrape(req, resp) - } + go t.backend.AfterScrape(context.TODO(), req, resp) return