From c9fe95b103f8cd2d886afd37f70cc799bf3ee61f Mon Sep 17 00:00:00 2001 From: Jimmy Zelinskie Date: Tue, 9 Aug 2016 20:08:15 -0400 Subject: [PATCH] s/backend/middleware --- backend/backend.go | 122 ----------------------------- cmd/trakr/main.go | 31 ++++---- frontend/http/frontend.go | 16 ++-- frontend/udp/frontend.go | 15 ++-- {backend => middleware}/hooks.go | 16 +--- middleware/middleware.go | 109 ++++++++++++++++++++++++++ {backend => middleware}/storage.go | 2 +- 7 files changed, 142 insertions(+), 169 deletions(-) delete mode 100644 backend/backend.go rename {backend => middleware}/hooks.go (53%) create mode 100644 middleware/middleware.go rename {backend => middleware}/storage.go (99%) diff --git a/backend/backend.go b/backend/backend.go deleted file mode 100644 index 1a17679..0000000 --- a/backend/backend.go +++ /dev/null @@ -1,122 +0,0 @@ -// Copyright 2016 Jimmy Zelinskie -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -// Package backend implements the TrackerLogic interface by executing -// a series of middleware hooks. -package backend - -import ( - "log" - "time" - - "golang.org/x/net/context" - - "github.com/jzelinskie/trakr/bittorrent" - "github.com/jzelinskie/trakr/frontend" -) - -type BackendConfig struct { - AnnounceInterval time.Duration `yaml:"announce_interval"` -} - -var _ frontend.TrackerLogic = &Backend{} - -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 protocol-agnostic backend of a BitTorrent tracker. -type Backend struct { - 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) { - 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) { - 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) { - 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) { - 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/cmd/trakr/main.go b/cmd/trakr/main.go index 8527baf..1c7e2e8 100644 --- a/cmd/trakr/main.go +++ b/cmd/trakr/main.go @@ -14,17 +14,17 @@ import ( "github.com/spf13/cobra" "gopkg.in/yaml.v2" - "github.com/jzelinskie/trakr/backend" httpfrontend "github.com/jzelinskie/trakr/frontend/http" udpfrontend "github.com/jzelinskie/trakr/frontend/udp" + "github.com/jzelinskie/trakr/middleware" ) type ConfigFile struct { - Config struct { - PrometheusAddr string `yaml:"prometheus_addr"` - backend.BackendConfig - HTTPConfig httpfrontend.Config `yaml:"http"` - UDPConfig udpfrontend.Config `yaml:"udp"` + MainConfigBlock struct { + PrometheusAddr string `yaml:"prometheus_addr"` + HTTPConfig httpfrontend.Config `yaml:"http"` + UDPConfig udpfrontend.Config `yaml:"udp"` + middleware.Config } `yaml:"trakr"` } @@ -81,13 +81,14 @@ func main() { if err != nil { return errors.New("failed to read config: " + err.Error()) } + cfg := configFile.MainConfigBlock go func() { promServer := http.Server{ - Addr: configFile.Config.PrometheusAddr, + Addr: cfg.PrometheusAddr, Handler: prometheus.Handler(), } - log.Println("started serving prometheus stats on", configFile.Config.PrometheusAddr) + log.Println("started serving prometheus stats on", cfg.PrometheusAddr) if err := promServer.ListenAndServe(); err != nil { log.Fatal(err) } @@ -95,7 +96,7 @@ func main() { // TODO create PeerStore // TODO create Hooks - trackerBackend, err := backend.New(configFile.Config.BackendConfig, nil, nil, nil, nil, nil) + logic := middleware.NewLogic(cfg.Config, nil, nil, nil, nil, nil) if err != nil { return err } @@ -106,24 +107,24 @@ func main() { var hFrontend *httpfrontend.Frontend var uFrontend *udpfrontend.Frontend - if configFile.Config.HTTPConfig.Addr != "" { + if cfg.HTTPConfig.Addr != "" { // TODO get the real TrackerLogic - hFrontend = httpfrontend.NewFrontend(trackerBackend, configFile.Config.HTTPConfig) + hFrontend = httpfrontend.NewFrontend(logic, cfg.HTTPConfig) go func() { - log.Println("started serving HTTP on", configFile.Config.HTTPConfig.Addr) + log.Println("started serving HTTP on", cfg.HTTPConfig.Addr) if err := hFrontend.ListenAndServe(); err != nil { errChan <- errors.New("failed to cleanly shutdown HTTP frontend: " + err.Error()) } }() } - if configFile.Config.UDPConfig.Addr != "" { + if cfg.UDPConfig.Addr != "" { // TODO get the real TrackerLogic - uFrontend = udpfrontend.NewFrontend(trackerBackend, configFile.Config.UDPConfig) + uFrontend = udpfrontend.NewFrontend(logic, cfg.UDPConfig) go func() { - log.Println("started serving UDP on", configFile.Config.UDPConfig.Addr) + log.Println("started serving UDP on", cfg.UDPConfig.Addr) if err := uFrontend.ListenAndServe(); err != nil { errChan <- errors.New("failed to cleanly shutdown UDP frontend: " + err.Error()) } diff --git a/frontend/http/frontend.go b/frontend/http/frontend.go index 0ba228d..41b64ab 100644 --- a/frontend/http/frontend.go +++ b/frontend/http/frontend.go @@ -71,15 +71,15 @@ type Config struct { type Frontend struct { grace *graceful.Server - backend frontend.TrackerLogic + logic frontend.TrackerLogic Config } // NewFrontend allocates a new instance of a Frontend. -func NewFrontend(backend frontend.TrackerLogic, cfg Config) *Frontend { +func NewFrontend(logic frontend.TrackerLogic, cfg Config) *Frontend { return &Frontend{ - backend: backend, - Config: cfg, + logic: logic, + Config: cfg, } } @@ -150,7 +150,7 @@ func (t *Frontend) announceRoute(w http.ResponseWriter, r *http.Request, _ httpr return } - resp, err := t.backend.HandleAnnounce(context.TODO(), req) + resp, err := t.logic.HandleAnnounce(context.TODO(), req) if err != nil { WriteError(w, err) return @@ -162,7 +162,7 @@ func (t *Frontend) announceRoute(w http.ResponseWriter, r *http.Request, _ httpr return } - go t.backend.AfterAnnounce(context.TODO(), req, resp) + go t.logic.AfterAnnounce(context.TODO(), req, resp) } // scrapeRoute parses and responds to a Scrape by using t.TrackerLogic. @@ -177,7 +177,7 @@ func (t *Frontend) scrapeRoute(w http.ResponseWriter, r *http.Request, _ httprou return } - resp, err := t.backend.HandleScrape(context.TODO(), req) + resp, err := t.logic.HandleScrape(context.TODO(), req) if err != nil { WriteError(w, err) return @@ -189,5 +189,5 @@ func (t *Frontend) scrapeRoute(w http.ResponseWriter, r *http.Request, _ httprou return } - go t.backend.AfterScrape(context.TODO(), req, resp) + go t.logic.AfterScrape(context.TODO(), req, resp) } diff --git a/frontend/udp/frontend.go b/frontend/udp/frontend.go index a74432e..44c5552 100644 --- a/frontend/udp/frontend.go +++ b/frontend/udp/frontend.go @@ -74,15 +74,15 @@ type Frontend struct { closing chan struct{} wg sync.WaitGroup - backend frontend.TrackerLogic + logic frontend.TrackerLogic Config } // NewFrontend allocates a new instance of a Frontend. -func NewFrontend(backend frontend.TrackerLogic, cfg Config) *Frontend { +func NewFrontend(logic frontend.TrackerLogic, cfg Config) *Frontend { return &Frontend{ closing: make(chan struct{}), - backend: backend, + logic: logic, Config: cfg, } } @@ -221,7 +221,7 @@ func (t *Frontend) handleRequest(r Request, w ResponseWriter) (response []byte, } var resp *bittorrent.AnnounceResponse - resp, err = t.backend.HandleAnnounce(context.TODO(), req) + resp, err = t.logic.HandleAnnounce(context.TODO(), req) if err != nil { WriteError(w, txID, err) return @@ -229,8 +229,7 @@ func (t *Frontend) handleRequest(r Request, w ResponseWriter) (response []byte, WriteAnnounce(w, txID, resp) - // TODO(mrd0ll4r): evaluate if it's worth spawning another goroutine. - go t.backend.AfterAnnounce(context.TODO(), req, resp) + go t.logic.AfterAnnounce(context.TODO(), req, resp) return @@ -245,7 +244,7 @@ func (t *Frontend) handleRequest(r Request, w ResponseWriter) (response []byte, } var resp *bittorrent.ScrapeResponse - resp, err = t.backend.HandleScrape(context.TODO(), req) + resp, err = t.logic.HandleScrape(context.TODO(), req) if err != nil { WriteError(w, txID, err) return @@ -253,7 +252,7 @@ func (t *Frontend) handleRequest(r Request, w ResponseWriter) (response []byte, WriteScrape(w, txID, resp) - go t.backend.AfterScrape(context.TODO(), req, resp) + go t.logic.AfterScrape(context.TODO(), req, resp) return diff --git a/backend/hooks.go b/middleware/hooks.go similarity index 53% rename from backend/hooks.go rename to middleware/hooks.go index 2c1792a..73797e4 100644 --- a/backend/hooks.go +++ b/middleware/hooks.go @@ -1,18 +1,4 @@ -// Copyright 2016 Jimmy Zelinskie -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package backend +package middleware import ( "golang.org/x/net/context" diff --git a/middleware/middleware.go b/middleware/middleware.go new file mode 100644 index 0000000..91d1cc5 --- /dev/null +++ b/middleware/middleware.go @@ -0,0 +1,109 @@ +// Package middleware implements the TrackerLogic interface by executing +// a series of middleware hooks. +package middleware + +import ( + "log" + "time" + + "golang.org/x/net/context" + + "github.com/jzelinskie/trakr/bittorrent" + "github.com/jzelinskie/trakr/frontend" +) + +type Config struct { + AnnounceInterval time.Duration `yaml:"announce_interval"` +} + +var _ frontend.TrackerLogic = &Logic{} + +func NewLogic(config Config, peerStore PeerStore, announcePreHooks, announcePostHooks, scrapePreHooks, scrapePostHooks []Hook) *Logic { + l := &Logic{ + announceInterval: config.AnnounceInterval, + peerStore: peerStore, + announcePreHooks: announcePreHooks, + announcePostHooks: announcePostHooks, + scrapePreHooks: scrapePreHooks, + scrapePostHooks: scrapePostHooks, + } + + if len(l.announcePreHooks) == 0 { + l.announcePreHooks = []Hook{nopHook{}} + } + + if len(l.announcePostHooks) == 0 { + l.announcePostHooks = []Hook{nopHook{}} + } + + if len(l.scrapePreHooks) == 0 { + l.scrapePreHooks = []Hook{nopHook{}} + } + + if len(l.scrapePostHooks) == 0 { + l.scrapePostHooks = []Hook{nopHook{}} + } + + return l +} + +// Logic is an implementation of the TrackerLogic that functions by +// executing a series of middleware hooks. +type Logic struct { + announceInterval time.Duration + peerStore PeerStore + announcePreHooks []Hook + announcePostHooks []Hook + scrapePreHooks []Hook + scrapePostHooks []Hook +} + +// HandleAnnounce generates a response for an Announce. +func (l *Logic) HandleAnnounce(ctx context.Context, req *bittorrent.AnnounceRequest) (*bittorrent.AnnounceResponse, error) { + resp := &bittorrent.AnnounceResponse{ + Interval: l.announceInterval, + } + for _, h := range l.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 (l *Logic) AfterAnnounce(ctx context.Context, req *bittorrent.AnnounceRequest, resp *bittorrent.AnnounceResponse) { + for _, h := range l.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 (l *Logic) HandleScrape(ctx context.Context, req *bittorrent.ScrapeRequest) (*bittorrent.ScrapeResponse, error) { + resp := &bittorrent.ScrapeResponse{ + Files: make(map[bittorrent.InfoHash]bittorrent.Scrape), + } + for _, h := range l.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 (l *Logic) AfterScrape(ctx context.Context, req *bittorrent.ScrapeRequest, resp *bittorrent.ScrapeResponse) { + for _, h := range l.scrapePostHooks { + if err := h.HandleScrape(ctx, req, resp); err != nil { + log.Println("trakr: post-scrape hooks failed:", err.Error()) + return + } + } +} diff --git a/backend/storage.go b/middleware/storage.go similarity index 99% rename from backend/storage.go rename to middleware/storage.go index 7761613..f1c6e92 100644 --- a/backend/storage.go +++ b/middleware/storage.go @@ -1,4 +1,4 @@ -package backend +package middleware import ( "time"