From 8f67c1018e551c735fae750d85bb6be5a841c26d Mon Sep 17 00:00:00 2001 From: Leo Balduf <balduf@hm.edu> Date: Sat, 6 Aug 2016 22:41:33 -0400 Subject: [PATCH] restructure into backend and frontends --- tracker.go => backend/backend.go | 56 ++++++++++----- hooks.go => backend/hooks.go | 2 +- storage.go => backend/storage.go | 2 +- bittorrent/bittorrent.go | 26 ------- cmd/trakr/main.go | 72 ++++++++++++++++--- .../http/bencode/bencode.go | 0 .../http/bencode/decoder.go | 0 .../http/bencode/decoder_test.go | 0 .../http/bencode/encoder.go | 0 .../http/bencode/encoder_test.go | 0 .../tracker.go => frontends/http/frontend.go | 32 ++++----- {bittorrent => frontends}/http/parser.go | 0 .../http/query_params.go | 0 .../http/query_params_test.go | 0 {bittorrent => frontends}/http/writer.go | 2 +- {bittorrent => frontends}/http/writer_test.go | 0 .../udp/bytepool/bytepool.go | 0 .../udp/connection_id.go | 0 .../udp/connection_id_test.go | 0 .../tracker.go => frontends/udp/frontend.go | 23 +++--- {bittorrent => frontends}/udp/parser.go | 0 {bittorrent => frontends}/udp/writer.go | 0 22 files changed, 132 insertions(+), 83 deletions(-) rename tracker.go => backend/backend.go (50%) rename hooks.go => backend/hooks.go (99%) rename storage.go => backend/storage.go (99%) rename {bittorrent => frontends}/http/bencode/bencode.go (100%) rename {bittorrent => frontends}/http/bencode/decoder.go (100%) rename {bittorrent => frontends}/http/bencode/decoder_test.go (100%) rename {bittorrent => frontends}/http/bencode/encoder.go (100%) rename {bittorrent => frontends}/http/bencode/encoder_test.go (100%) rename bittorrent/http/tracker.go => frontends/http/frontend.go (86%) rename {bittorrent => frontends}/http/parser.go (100%) rename {bittorrent => frontends}/http/query_params.go (100%) rename {bittorrent => frontends}/http/query_params_test.go (100%) rename {bittorrent => frontends}/http/writer.go (98%) rename {bittorrent => frontends}/http/writer_test.go (100%) rename {bittorrent => frontends}/udp/bytepool/bytepool.go (100%) rename {bittorrent => frontends}/udp/connection_id.go (100%) rename {bittorrent => frontends}/udp/connection_id_test.go (100%) rename bittorrent/udp/tracker.go => frontends/udp/frontend.go (91%) rename {bittorrent => frontends}/udp/parser.go (100%) rename {bittorrent => frontends}/udp/writer.go (100%) diff --git a/tracker.go b/backend/backend.go similarity index 50% rename from tracker.go rename to backend/backend.go index a4dab7c..2e00989 100644 --- a/tracker.go +++ b/backend/backend.go @@ -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) diff --git a/hooks.go b/backend/hooks.go similarity index 99% rename from hooks.go rename to backend/hooks.go index 03c4430..484c4b9 100644 --- a/hooks.go +++ b/backend/hooks.go @@ -12,7 +12,7 @@ // See the License for the specific language governing permissions and // limitations under the License. -package trakr +package backend import ( "fmt" diff --git a/storage.go b/backend/storage.go similarity index 99% rename from storage.go rename to backend/storage.go index 02c719a..541742f 100644 --- a/storage.go +++ b/backend/storage.go @@ -1,4 +1,4 @@ -package trakr +package backend import ( "fmt" diff --git a/bittorrent/bittorrent.go b/bittorrent/bittorrent.go index 34294fe..20c9b66 100644 --- a/bittorrent/bittorrent.go +++ b/bittorrent/bittorrent.go @@ -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 -} diff --git a/cmd/trakr/main.go b/cmd/trakr/main.go index f250c87..f44d764 100644 --- a/cmd/trakr/main.go +++ b/cmd/trakr/main.go @@ -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 diff --git a/bittorrent/http/bencode/bencode.go b/frontends/http/bencode/bencode.go similarity index 100% rename from bittorrent/http/bencode/bencode.go rename to frontends/http/bencode/bencode.go diff --git a/bittorrent/http/bencode/decoder.go b/frontends/http/bencode/decoder.go similarity index 100% rename from bittorrent/http/bencode/decoder.go rename to frontends/http/bencode/decoder.go diff --git a/bittorrent/http/bencode/decoder_test.go b/frontends/http/bencode/decoder_test.go similarity index 100% rename from bittorrent/http/bencode/decoder_test.go rename to frontends/http/bencode/decoder_test.go diff --git a/bittorrent/http/bencode/encoder.go b/frontends/http/bencode/encoder.go similarity index 100% rename from bittorrent/http/bencode/encoder.go rename to frontends/http/bencode/encoder.go diff --git a/bittorrent/http/bencode/encoder_test.go b/frontends/http/bencode/encoder_test.go similarity index 100% rename from bittorrent/http/bencode/encoder_test.go rename to frontends/http/bencode/encoder_test.go diff --git a/bittorrent/http/tracker.go b/frontends/http/frontend.go similarity index 86% rename from bittorrent/http/tracker.go rename to frontends/http/frontend.go index f029de2..716da66 100644 --- a/bittorrent/http/tracker.go +++ b/frontends/http/frontend.go @@ -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)) diff --git a/bittorrent/http/parser.go b/frontends/http/parser.go similarity index 100% rename from bittorrent/http/parser.go rename to frontends/http/parser.go diff --git a/bittorrent/http/query_params.go b/frontends/http/query_params.go similarity index 100% rename from bittorrent/http/query_params.go rename to frontends/http/query_params.go diff --git a/bittorrent/http/query_params_test.go b/frontends/http/query_params_test.go similarity index 100% rename from bittorrent/http/query_params_test.go rename to frontends/http/query_params_test.go diff --git a/bittorrent/http/writer.go b/frontends/http/writer.go similarity index 98% rename from bittorrent/http/writer.go rename to frontends/http/writer.go index c1e9266..0565fc3 100644 --- a/bittorrent/http/writer.go +++ b/frontends/http/writer.go @@ -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. diff --git a/bittorrent/http/writer_test.go b/frontends/http/writer_test.go similarity index 100% rename from bittorrent/http/writer_test.go rename to frontends/http/writer_test.go diff --git a/bittorrent/udp/bytepool/bytepool.go b/frontends/udp/bytepool/bytepool.go similarity index 100% rename from bittorrent/udp/bytepool/bytepool.go rename to frontends/udp/bytepool/bytepool.go diff --git a/bittorrent/udp/connection_id.go b/frontends/udp/connection_id.go similarity index 100% rename from bittorrent/udp/connection_id.go rename to frontends/udp/connection_id.go diff --git a/bittorrent/udp/connection_id_test.go b/frontends/udp/connection_id_test.go similarity index 100% rename from bittorrent/udp/connection_id_test.go rename to frontends/udp/connection_id_test.go diff --git a/bittorrent/udp/tracker.go b/frontends/udp/frontend.go similarity index 91% rename from bittorrent/udp/tracker.go rename to frontends/udp/frontend.go index 2ff6ac6..7da88f7 100644 --- a/bittorrent/udp/tracker.go +++ b/frontends/udp/frontend.go @@ -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. diff --git a/bittorrent/udp/parser.go b/frontends/udp/parser.go similarity index 100% rename from bittorrent/udp/parser.go rename to frontends/udp/parser.go diff --git a/bittorrent/udp/writer.go b/frontends/udp/writer.go similarity index 100% rename from bittorrent/udp/writer.go rename to frontends/udp/writer.go