mirror of
https://github.com/sot-tech/mochi.git
synced 2026-06-22 03:58:52 -07:00
(wip) rework configuration to support multiple frontends
This commit is contained in:
@@ -39,10 +39,10 @@ type hook struct {
|
||||
unapproved map[ClientID]struct{}
|
||||
}
|
||||
|
||||
func build(options conf.MapConfig, _ storage.PeerStorage) (middleware.Hook, error) {
|
||||
func build(config conf.MapConfig, _ storage.PeerStorage) (middleware.Hook, error) {
|
||||
var cfg Config
|
||||
|
||||
if err := options.Unmarshal(&cfg); err != nil {
|
||||
if err := config.Unmarshal(&cfg); err != nil {
|
||||
return nil, fmt.Errorf("middleware %s: %w", Name, err)
|
||||
}
|
||||
|
||||
|
||||
+1
-1
@@ -19,7 +19,7 @@ type Hook interface {
|
||||
}
|
||||
|
||||
// Pinger is an optional interface that may be implemented by a pre Hook
|
||||
// to check if it is operational. Used in frontend.TrackerLogic.
|
||||
// to check if it is operational. Used in frontend.Logic.
|
||||
//
|
||||
// It may be useful in cases when Hook performs foreign requests to
|
||||
// some external resources (i.e. storage) and `ping` request should
|
||||
|
||||
@@ -70,12 +70,10 @@ type hook struct {
|
||||
jwks *keyfunc.JWKS
|
||||
}
|
||||
|
||||
func build(options conf.MapConfig, _ storage.PeerStorage) (h middleware.Hook, err error) {
|
||||
func build(config conf.MapConfig, _ storage.PeerStorage) (h middleware.Hook, err error) {
|
||||
var cfg Config
|
||||
|
||||
logger.Debug().Object("options", options).Msg("creating new JWT middleware")
|
||||
|
||||
if err = options.Unmarshal(&cfg); err != nil {
|
||||
if err = config.Unmarshal(&cfg); err != nil {
|
||||
return nil, fmt.Errorf("middleware %s: %w", Name, err)
|
||||
}
|
||||
|
||||
|
||||
+21
-22
@@ -5,18 +5,22 @@ import (
|
||||
"time"
|
||||
|
||||
"github.com/sot-tech/mochi/bittorrent"
|
||||
"github.com/sot-tech/mochi/frontend"
|
||||
"github.com/sot-tech/mochi/pkg/log"
|
||||
"github.com/sot-tech/mochi/pkg/stop"
|
||||
"github.com/sot-tech/mochi/storage"
|
||||
)
|
||||
|
||||
var (
|
||||
logger = log.NewLogger("middleware")
|
||||
_ frontend.TrackerLogic = &Logic{}
|
||||
)
|
||||
// Logic used by a frontend in order to: (1) generate a
|
||||
// response from a parsed request, and (2) asynchronously observe anything
|
||||
// after the response has been delivered to the client.
|
||||
type Logic struct {
|
||||
announceInterval time.Duration
|
||||
minAnnounceInterval time.Duration
|
||||
preHooks []Hook
|
||||
postHooks []Hook
|
||||
pingers []Pinger
|
||||
}
|
||||
|
||||
// NewLogic creates a new instance of a TrackerLogic that executes the provided
|
||||
// NewLogic creates a new instance of a Logic that executes the provided
|
||||
// middleware hooks.
|
||||
func NewLogic(annInterval, minAnnInterval time.Duration, peerStore storage.PeerStorage, preHooks, postHooks []Hook) *Logic {
|
||||
l := &Logic{
|
||||
@@ -34,17 +38,10 @@ func NewLogic(annInterval, minAnnInterval time.Duration, peerStore storage.PeerS
|
||||
return l
|
||||
}
|
||||
|
||||
// Logic is an implementation of the TrackerLogic that functions by
|
||||
// executing a series of middleware hooks.
|
||||
type Logic struct {
|
||||
announceInterval time.Duration
|
||||
minAnnounceInterval time.Duration
|
||||
preHooks []Hook
|
||||
postHooks []Hook
|
||||
pingers []Pinger
|
||||
}
|
||||
|
||||
// HandleAnnounce generates a response for an Announce.
|
||||
//
|
||||
// Returns the updated context, the generated AnnounceResponse and no error
|
||||
// on success; nil and error on failure.
|
||||
func (l *Logic) HandleAnnounce(ctx context.Context, req *bittorrent.AnnounceRequest) (_ context.Context, resp *bittorrent.AnnounceResponse, err error) {
|
||||
logger.Debug().Object("request", req).Msg("new announce request")
|
||||
resp = &bittorrent.AnnounceResponse{
|
||||
@@ -62,8 +59,8 @@ func (l *Logic) HandleAnnounce(ctx context.Context, req *bittorrent.AnnounceRequ
|
||||
return ctx, resp, nil
|
||||
}
|
||||
|
||||
// AfterAnnounce does something with the results of an Announce after it has
|
||||
// been completed.
|
||||
// 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) {
|
||||
var err error
|
||||
for _, h := range l.postHooks {
|
||||
@@ -78,6 +75,9 @@ func (l *Logic) AfterAnnounce(ctx context.Context, req *bittorrent.AnnounceReque
|
||||
}
|
||||
|
||||
// HandleScrape generates a response for a Scrape.
|
||||
//
|
||||
// Returns the updated context, the generated AnnounceResponse and no error
|
||||
// on success; nil and error on failure.
|
||||
func (l *Logic) HandleScrape(ctx context.Context, req *bittorrent.ScrapeRequest) (_ context.Context, resp *bittorrent.ScrapeResponse, err error) {
|
||||
logger.Debug().Object("request", req).Msg("new scrape request")
|
||||
resp = &bittorrent.ScrapeResponse{
|
||||
@@ -93,8 +93,7 @@ func (l *Logic) HandleScrape(ctx context.Context, req *bittorrent.ScrapeRequest)
|
||||
return ctx, resp, nil
|
||||
}
|
||||
|
||||
// AfterScrape does something with the results of a Scrape after it has been
|
||||
// completed.
|
||||
// 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) {
|
||||
var err error
|
||||
for _, h := range l.postHooks {
|
||||
@@ -109,7 +108,7 @@ func (l *Logic) AfterScrape(ctx context.Context, req *bittorrent.ScrapeRequest,
|
||||
}
|
||||
}
|
||||
|
||||
// Ping performs check if all Hook-s are operational
|
||||
// Ping executes checks if all Hook-s are operational
|
||||
func (l *Logic) Ping(ctx context.Context) (err error) {
|
||||
for _, p := range l.pingers {
|
||||
if err = p.Ping(ctx); err != nil {
|
||||
|
||||
+26
-53
@@ -1,92 +1,65 @@
|
||||
// Package middleware implements the TrackerLogic interface by executing
|
||||
// Package middleware implements the Logic interface by executing
|
||||
// a series of middleware hooks.
|
||||
package middleware
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"sync"
|
||||
|
||||
"github.com/sot-tech/mochi/pkg/conf"
|
||||
"github.com/sot-tech/mochi/pkg/log"
|
||||
"github.com/sot-tech/mochi/storage"
|
||||
)
|
||||
|
||||
var (
|
||||
driversM sync.RWMutex
|
||||
drivers = make(map[string]Builder)
|
||||
|
||||
// ErrBuilderDoesNotExist is the error returned by NewMiddleware when a
|
||||
// middleware driver with that name does not exist.
|
||||
ErrBuilderDoesNotExist = errors.New("middleware builder with that name does not exist")
|
||||
logger = log.NewLogger("middleware")
|
||||
buildersMU sync.RWMutex
|
||||
builders = make(map[string]Builder)
|
||||
)
|
||||
|
||||
// Builder is the interface used to initialize a new type of middleware.
|
||||
//
|
||||
// The `options` parameter is map of parameters that should be unmarshalled into
|
||||
// the hook's custom configuration.
|
||||
type Builder func(options conf.MapConfig, storage storage.PeerStorage) (Hook, error)
|
||||
// Builder is the function used to initialize a new Hook
|
||||
// with provided configuration.
|
||||
type Builder func(conf.MapConfig, storage.PeerStorage) (Hook, error)
|
||||
|
||||
// RegisterBuilder makes a Builder available by the provided name.
|
||||
//
|
||||
// If called twice with the same name, the name is blank, or if the provided
|
||||
// Builder is nil, this function panics.
|
||||
func RegisterBuilder(name string, d Builder) {
|
||||
func RegisterBuilder(name string, b Builder) {
|
||||
if name == "" {
|
||||
panic("middleware: could not register a Builder with an empty name")
|
||||
panic("middleware: could not register Builder with an empty name")
|
||||
}
|
||||
if d == nil {
|
||||
if b == nil {
|
||||
panic("middleware: could not register a nil Builder")
|
||||
}
|
||||
|
||||
driversM.Lock()
|
||||
defer driversM.Unlock()
|
||||
buildersMU.Lock()
|
||||
defer buildersMU.Unlock()
|
||||
|
||||
if _, dup := drivers[name]; dup {
|
||||
if _, dup := builders[name]; dup {
|
||||
panic("middleware: RegisterBuilder called twice for " + name)
|
||||
}
|
||||
|
||||
drivers[name] = d
|
||||
}
|
||||
|
||||
// NewHook attempts to initialize a new middleware instance from the
|
||||
// list of registered Builders.
|
||||
//
|
||||
// If a driver does not exist, returns ErrBuilderDoesNotExist.
|
||||
func NewHook(name string, options conf.MapConfig, storage storage.PeerStorage) (Hook, error) {
|
||||
driversM.RLock()
|
||||
defer driversM.RUnlock()
|
||||
|
||||
var newHook Builder
|
||||
newHook, ok := drivers[name]
|
||||
if !ok {
|
||||
return nil, ErrBuilderDoesNotExist
|
||||
}
|
||||
|
||||
return newHook(options, storage)
|
||||
}
|
||||
|
||||
// Config is the generic configuration format used for all registered Hooks.
|
||||
type Config struct {
|
||||
Name string
|
||||
Options conf.MapConfig
|
||||
builders[name] = b
|
||||
}
|
||||
|
||||
// NewHooks is a utility function for initializing Hooks in bulk.
|
||||
// each element of configs must contain pairs `name` - string and `options` - map[string]any
|
||||
func NewHooks(configs []conf.MapConfig, storage storage.PeerStorage) (hooks []Hook, err error) {
|
||||
for _, cfg := range configs {
|
||||
var c Config
|
||||
|
||||
if err = cfg.Unmarshal(&c); err != nil {
|
||||
func NewHooks(configs []conf.NamedMapConfig, storage storage.PeerStorage) (hooks []Hook, err error) {
|
||||
buildersMU.RLock()
|
||||
defer buildersMU.RUnlock()
|
||||
for _, c := range configs {
|
||||
logger.Debug().Object("hook", c).Msg("starting hook")
|
||||
newHook, ok := builders[c.Name]
|
||||
if !ok {
|
||||
err = fmt.Errorf("hook with name '%s' does not exists", c.Name)
|
||||
break
|
||||
}
|
||||
|
||||
var h Hook
|
||||
h, err = NewHook(c.Name, c.Options, storage)
|
||||
if err != nil {
|
||||
if h, err = newHook(c.Config, storage); err != nil {
|
||||
break
|
||||
}
|
||||
|
||||
hooks = append(hooks, h)
|
||||
logger.Info().Str("name", c.Name).Msg("hook started")
|
||||
}
|
||||
|
||||
return
|
||||
|
||||
@@ -38,18 +38,18 @@ type baseConfig struct {
|
||||
Configuration conf.MapConfig
|
||||
}
|
||||
|
||||
func build(options conf.MapConfig, st storage.PeerStorage) (h middleware.Hook, err error) {
|
||||
func build(config conf.MapConfig, st storage.PeerStorage) (h middleware.Hook, err error) {
|
||||
var cfg baseConfig
|
||||
if err = options.Unmarshal(&cfg); err != nil {
|
||||
if err = config.Unmarshal(&cfg); err != nil {
|
||||
return nil, fmt.Errorf("middleware %s: %w", Name, err)
|
||||
}
|
||||
|
||||
if len(cfg.Source) == 0 {
|
||||
return nil, fmt.Errorf("invalid options for middleware %s: source not provided", Name)
|
||||
return nil, fmt.Errorf("invalid config for middleware %s: source not provided", Name)
|
||||
}
|
||||
|
||||
if cfg.Configuration == nil {
|
||||
return nil, fmt.Errorf("invalid options for middleware %s: options not provided", Name)
|
||||
return nil, fmt.Errorf("invalid config for middleware %s: config not provided", Name)
|
||||
}
|
||||
|
||||
var ds storage.DataStorage = st
|
||||
|
||||
@@ -23,10 +23,10 @@ func init() {
|
||||
middleware.RegisterBuilder(Name, build)
|
||||
}
|
||||
|
||||
func build(options conf.MapConfig, _ storage.PeerStorage) (h middleware.Hook, err error) {
|
||||
func build(config conf.MapConfig, _ storage.PeerStorage) (h middleware.Hook, err error) {
|
||||
var cfg Config
|
||||
|
||||
if err = options.Unmarshal(&cfg); err != nil {
|
||||
if err = config.Unmarshal(&cfg); err != nil {
|
||||
err = fmt.Errorf("middleware %s: %w", Name, err)
|
||||
} else {
|
||||
if err := checkConfig(cfg); err == nil {
|
||||
|
||||
Reference in New Issue
Block a user