From cecd07c1139e6a52a776ab6badb16ece62a2489d Mon Sep 17 00:00:00 2001 From: ErickSkrauch Date: Wed, 7 Feb 2024 17:34:57 +0100 Subject: [PATCH] Remove dispatcher and eventsubscribers modules, remove statsd integration, remove event bus usage. Overall cleanup before otel integration --- go.mod | 4 - go.sum | 6 - internal/db/redis/redis.go | 4 +- internal/db/redis/redis_integration_test.go | 2 +- internal/di/config.go | 2 +- internal/di/db.go | 13 +- internal/di/di.go | 15 +- internal/di/dispatcher.go | 34 --- internal/di/handlers.go | 26 +- internal/di/logger.go | 38 +-- internal/di/{mojang_textures.go => mojang.go} | 2 +- internal/di/profiles.go | 2 +- internal/di/server.go | 2 +- internal/dispatcher/dispatcher.go | 34 --- internal/eventsubscribers/health_checkers.go | 60 ----- .../eventsubscribers/health_checkers_test.go | 50 ---- internal/eventsubscribers/logger.go | 41 --- internal/eventsubscribers/logger_test.go | 159 ------------ internal/eventsubscribers/stats_reporter.go | 116 --------- .../eventsubscribers/stats_reporter_test.go | 240 ------------------ internal/eventsubscribers/subscriber.go | 7 - internal/http/http.go | 19 +- internal/http/http_test.go | 16 +- 23 files changed, 29 insertions(+), 863 deletions(-) delete mode 100644 internal/di/dispatcher.go rename internal/di/{mojang_textures.go => mojang.go} (98%) delete mode 100644 internal/dispatcher/dispatcher.go delete mode 100644 internal/eventsubscribers/health_checkers.go delete mode 100644 internal/eventsubscribers/health_checkers_test.go delete mode 100644 internal/eventsubscribers/logger.go delete mode 100644 internal/eventsubscribers/logger_test.go delete mode 100644 internal/eventsubscribers/stats_reporter.go delete mode 100644 internal/eventsubscribers/stats_reporter_test.go delete mode 100644 internal/eventsubscribers/subscriber.go diff --git a/go.mod b/go.mod index 97f3f71..baa7091 100644 --- a/go.mod +++ b/go.mod @@ -2,11 +2,8 @@ module ely.by/chrly go 1.21 -replace github.com/asaskevich/EventBus v0.0.0-20200330115301-33b3bc6a7ddc => github.com/erickskrauch/EventBus v0.0.0-20200330115301-33b3bc6a7ddc - // Main dependencies require ( - github.com/asaskevich/EventBus v0.0.0-20200330115301-33b3bc6a7ddc github.com/brunomvsouza/singleflight v0.4.0 github.com/defval/di v1.12.0 github.com/etherlabsio/healthcheck/v2 v2.0.0 @@ -42,7 +39,6 @@ require ( github.com/leodido/go-urn v1.2.4 // indirect github.com/magiconair/properties v1.8.7 // indirect github.com/mitchellh/mapstructure v1.5.0 // indirect - github.com/mono83/udpwriter v1.0.2 // indirect github.com/pelletier/go-toml/v2 v2.1.1 // indirect github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect github.com/sagikazarmark/locafero v0.4.0 // indirect diff --git a/go.sum b/go.sum index 3a1ef38..81bc695 100644 --- a/go.sum +++ b/go.sum @@ -9,8 +9,6 @@ github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1 github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/defval/di v1.12.0 h1:xXm7BMX2+Nr0Yyu55DeJl/rmfCA7CQX89f4AGE0zA6U= github.com/defval/di v1.12.0/go.mod h1:PhVbOxQOvU7oawTOJXXTvqOJp1Dvsjs5PuzMw9gGl0I= -github.com/erickskrauch/EventBus v0.0.0-20200330115301-33b3bc6a7ddc h1:kz3f5uMA1LxfRvJjZmMYG7Zu2rddTfJy6QZofz2YoGQ= -github.com/erickskrauch/EventBus v0.0.0-20200330115301-33b3bc6a7ddc/go.mod h1:RHSo3YFV/SbOGyFR36RKWaXPy3g9nKAmn6ebNLpbco4= github.com/etherlabsio/healthcheck/v2 v2.0.0 h1:oKq8cbpwM/yNGPXf2Sff6MIjVUjx/pGYFydWzeK2MpA= github.com/etherlabsio/healthcheck/v2 v2.0.0/go.mod h1:huNVOjKzu6FI1eaO1CGD3ZjhrmPWf5Obu/pzpI6/wog= github.com/frankban/quicktest v1.14.6 h1:7Xjx+VpznH+oBnejlPUj8oUpdxnVs4f8XU8WnHkI4W8= @@ -59,8 +57,6 @@ github.com/mitchellh/mapstructure v1.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyua github.com/mitchellh/mapstructure v1.5.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= github.com/mono83/slf v0.0.0-20170919161409-79153e9636db h1:tlz4fTklh5mttoq5M+0yEc5Lap8W/02A2HCXCJn5iz0= github.com/mono83/slf v0.0.0-20170919161409-79153e9636db/go.mod h1:MfF+zNMZz+5IGY9h8jpFaGLyGoJ2ZPri2FmUVftBoUU= -github.com/mono83/udpwriter v1.0.2 h1:JiQ/N646oZoJA1G0FOMvn2teMt6SdL1KwNH2mszOlQs= -github.com/mono83/udpwriter v1.0.2/go.mod h1:mTDiyLtA0tXoxckkV9T4NUkJTgSQIuO8pAUKx/dSRkQ= github.com/nbio/st v0.0.0-20140626010706-e9e8d9816f32 h1:W6apQkHrMkS0Muv8G/TipAy/FJl/rCYT0+EuS8+Z0z4= github.com/nbio/st v0.0.0-20140626010706-e9e8d9816f32/go.mod h1:9wM+0iRr9ahx58uYLpLIr5fm8diHn0JbqRycJi6w0Ms= github.com/pelletier/go-toml/v2 v2.1.1 h1:LWAJwfNvjQZCFIDKWYQaM62NcYeYViCmWIwmOStowAI= @@ -92,7 +88,6 @@ github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSS github.com/stretchr/objx v0.5.0 h1:1zr/of2m5FGMsad5YfcqgdqdWrIhu+EBEJRhR1U7z/c= github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= -github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= github.com/stretchr/testify v1.8.2/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= @@ -126,6 +121,5 @@ gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8 gopkg.in/ini.v1 v1.67.0 h1:Dgnx+6+nfE+IfzjUEISNeydPJh9AXNNsWbGP9KzCsOA= gopkg.in/ini.v1 v1.67.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= -gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/internal/db/redis/redis.go b/internal/db/redis/redis.go index 14fe867..c9eb9cc 100644 --- a/internal/db/redis/redis.go +++ b/internal/db/redis/redis.go @@ -215,8 +215,8 @@ func storeMojangUuid(ctx context.Context, conn radix.Conn, username string, uuid return nil } -func (r *Redis) Ping() error { - return r.client.Do(r.context, radix.Cmd(nil, "PING")) +func (r *Redis) Ping(ctx context.Context) error { + return r.client.Do(ctx, radix.Cmd(nil, "PING")) } func normalizeUuid(uuid string) string { diff --git a/internal/db/redis/redis_integration_test.go b/internal/db/redis/redis_integration_test.go index 1dc13e9..0ee0894 100644 --- a/internal/db/redis/redis_integration_test.go +++ b/internal/db/redis/redis_integration_test.go @@ -294,6 +294,6 @@ func (s *redisTestSuite) TestStoreUuid() { } func (s *redisTestSuite) TestPing() { - err := s.Redis.Ping() + err := s.Redis.Ping(context.Background()) s.Require().Nil(err) } diff --git a/internal/di/config.go b/internal/di/config.go index 577fdbb..93664f3 100644 --- a/internal/di/config.go +++ b/internal/di/config.go @@ -5,7 +5,7 @@ import ( "github.com/spf13/viper" ) -var config = di.Options( +var configDiOptions = di.Options( di.Provide(newConfig), ) diff --git a/internal/di/db.go b/internal/di/db.go index 80fe7ec..6f098bf 100644 --- a/internal/di/db.go +++ b/internal/di/db.go @@ -5,21 +5,18 @@ import ( "fmt" "github.com/defval/di" + "github.com/etherlabsio/healthcheck/v2" "github.com/spf13/viper" - db2 "ely.by/chrly/internal/db" + "ely.by/chrly/internal/db" "ely.by/chrly/internal/db/redis" - es "ely.by/chrly/internal/eventsubscribers" "ely.by/chrly/internal/mojang" "ely.by/chrly/internal/profiles" ) -// v4 had the idea that it would be possible to separate backends for storing skins and capes. -// But in v5 the storage will be unified, so this is just temporary constructors before large reworking. -// // Since there are no options for selecting target backends, // all constants in this case point to static specific implementations. -var db = di.Options( +var dbDeOptions = di.Options( di.Provide(newRedis, di.As(new(profiles.ProfilesRepository)), di.As(new(profiles.ProfilesFinder)), @@ -34,7 +31,7 @@ func newRedis(container *di.Container, config *viper.Viper) (*redis.Redis, error conn, err := redis.New( context.Background(), - db2.NewZlibEncoder(&db2.JsonSerializer{}), + db.NewZlibEncoder(&db.JsonSerializer{}), fmt.Sprintf("%s:%d", config.GetString("storage.redis.host"), config.GetInt("storage.redis.port")), config.GetInt("storage.redis.poolSize"), ) @@ -45,7 +42,7 @@ func newRedis(container *di.Container, config *viper.Viper) (*redis.Redis, error if err := container.Provide(func() *namedHealthChecker { return &namedHealthChecker{ Name: "redis", - Checker: es.DatabaseChecker(conn), + Checker: healthcheck.CheckerFunc(conn.Ping), } }); err != nil { return nil, err diff --git a/internal/di/di.go b/internal/di/di.go index 4771b79..4036b66 100644 --- a/internal/di/di.go +++ b/internal/di/di.go @@ -4,14 +4,13 @@ import "github.com/defval/di" func New() (*di.Container, error) { return di.New( - config, - dispatcher, - logger, - db, - mojangTextures, - handlers, - profilesDi, - server, + configDiOptions, + loggerDiOptions, + dbDeOptions, + mojangDiOptions, + handlersDiOptions, + profilesDiOptions, + serverDiOptions, securityDiOptions, ) } diff --git a/internal/di/dispatcher.go b/internal/di/dispatcher.go deleted file mode 100644 index f0a3131..0000000 --- a/internal/di/dispatcher.go +++ /dev/null @@ -1,34 +0,0 @@ -package di - -import ( - "github.com/defval/di" - "github.com/mono83/slf" - - d "ely.by/chrly/internal/dispatcher" - "ely.by/chrly/internal/eventsubscribers" - "ely.by/chrly/internal/http" -) - -var dispatcher = di.Options( - di.Provide(newDispatcher, - di.As(new(d.Emitter)), - di.As(new(d.Subscriber)), - di.As(new(http.Emitter)), - di.As(new(eventsubscribers.Subscriber)), - ), - di.Invoke(enableEventsHandlers), -) - -func newDispatcher() d.Dispatcher { - return d.New() -} - -func enableEventsHandlers( - dispatcher d.Subscriber, - logger slf.Logger, - statsReporter slf.StatsReporter, -) { - // TODO: use idea from https://github.com/defval/di/issues/10#issuecomment-615869852 - (&eventsubscribers.Logger{Logger: logger}).ConfigureWithDispatcher(dispatcher) - (&eventsubscribers.StatsReporter{StatsReporter: statsReporter}).ConfigureWithDispatcher(dispatcher) -} diff --git a/internal/di/handlers.go b/internal/di/handlers.go index 4f466e7..6fe7125 100644 --- a/internal/di/handlers.go +++ b/internal/di/handlers.go @@ -1,8 +1,8 @@ package di import ( - "errors" "net/http" + "slices" "strings" "github.com/defval/di" @@ -13,7 +13,7 @@ import ( . "ely.by/chrly/internal/http" ) -var handlers = di.Options( +var handlersDiOptions = di.Options( di.Provide(newHandlerFactory, di.As(new(http.Handler))), di.Provide(newSkinsystemHandler, di.WithName("skinsystem")), di.Provide(newApiHandler, di.WithName("api")), @@ -22,7 +22,6 @@ var handlers = di.Options( func newHandlerFactory( container *di.Container, config *viper.Viper, - emitter Emitter, ) (*mux.Router, error) { enabledModules := config.GetStringSlice("modules") @@ -31,7 +30,7 @@ func newHandlerFactory( // if you set an empty prefix. Since the main application should be mounted at the root prefix, // we use it as the base router var router *mux.Router - if hasValue(enabledModules, "skinsystem") { + if slices.Contains(enabledModules, "skinsystem") { if err := container.Resolve(&router, di.Name("skinsystem")); err != nil { return nil, err } @@ -40,13 +39,13 @@ func newHandlerFactory( } router.StrictSlash(true) - requestEventsMiddleware := CreateRequestEventsMiddleware(emitter, "skinsystem") + requestEventsMiddleware := CreateRequestEventsMiddleware() router.Use(requestEventsMiddleware) // NotFoundHandler doesn't call for registered middlewares, so we must wrap it manually. // See https://github.com/gorilla/mux/issues/416#issuecomment-600079279 router.NotFoundHandler = requestEventsMiddleware(http.HandlerFunc(NotFoundHandler)) - if hasValue(enabledModules, "api") { + if slices.Contains(enabledModules, "api") { var apiRouter *mux.Router if err := container.Resolve(&apiRouter, di.Name("api")); err != nil { return nil, err @@ -62,11 +61,6 @@ func newHandlerFactory( mount(router, "/api", apiRouter) } - err := container.Invoke(enableReporters) - if err != nil && !errors.Is(err, di.ErrTypeNotExists) { - return nil, err - } - // Resolve health checkers last, because all the services required by the application // must first be initialized and each of them can publish its own checkers var healthCheckers []*namedHealthChecker @@ -108,16 +102,6 @@ func newApiHandler(profilesManager ProfilesManager) *mux.Router { }).Handler() } -func hasValue(slice []string, needle string) bool { - for _, value := range slice { - if value == needle { - return true - } - } - - return false -} - func mount(router *mux.Router, path string, handler http.Handler) { router.PathPrefix(path).Handler( http.StripPrefix( diff --git a/internal/di/logger.go b/internal/di/logger.go index 723e008..fabd93a 100644 --- a/internal/di/logger.go +++ b/internal/di/logger.go @@ -1,26 +1,21 @@ package di import ( - "os" - "github.com/defval/di" "github.com/getsentry/raven-go" "github.com/mono83/slf" "github.com/mono83/slf/rays" "github.com/mono83/slf/recievers/sentry" - "github.com/mono83/slf/recievers/statsd" "github.com/mono83/slf/recievers/writer" "github.com/mono83/slf/wd" "github.com/spf13/viper" - "ely.by/chrly/internal/eventsubscribers" "ely.by/chrly/internal/version" ) -var logger = di.Options( +var loggerDiOptions = di.Options( di.Provide(newLogger), di.Provide(newSentry), - di.Provide(newStatsReporter), ) type loggerParams struct { @@ -71,34 +66,3 @@ func newSentry(config *viper.Viper) (*raven.Client, error) { return ravenClient, nil } - -func newStatsReporter(config *viper.Viper) (slf.StatsReporter, error) { - dispatcher := &slf.Dispatcher{} - - statsdAddr := config.GetString("statsd.addr") - if statsdAddr != "" { - hostname, err := os.Hostname() - if err != nil { - return nil, err - } - - statsdReceiver, err := statsd.NewReceiver(statsd.Config{ - Address: statsdAddr, - Prefix: "ely.skinsystem." + hostname + ".app.", - FlushEvery: 1, - }) - if err != nil { - return nil, err - } - - dispatcher.AddReceiver(statsdReceiver) - } - - return wd.Custom("", "", dispatcher), nil -} - -func enableReporters(reporter slf.StatsReporter, factories []eventsubscribers.Reporter) { - for _, factory := range factories { - factory.Enable(reporter) - } -} diff --git a/internal/di/mojang_textures.go b/internal/di/mojang.go similarity index 98% rename from internal/di/mojang_textures.go rename to internal/di/mojang.go index 77170a3..be73086 100644 --- a/internal/di/mojang_textures.go +++ b/internal/di/mojang.go @@ -12,7 +12,7 @@ import ( "ely.by/chrly/internal/profiles" ) -var mojangTextures = di.Options( +var mojangDiOptions = di.Options( di.Provide(newMojangApi), di.Provide(newMojangTexturesProviderFactory), di.Provide(newMojangTexturesProvider), diff --git a/internal/di/profiles.go b/internal/di/profiles.go index 31a9853..56c436c 100644 --- a/internal/di/profiles.go +++ b/internal/di/profiles.go @@ -7,7 +7,7 @@ import ( "ely.by/chrly/internal/profiles" ) -var profilesDi = di.Options( +var profilesDiOptions = di.Options( di.Provide(newProfilesManager, di.As(new(ProfilesManager))), di.Provide(newProfilesProvider, di.As(new(ProfilesProvider))), ) diff --git a/internal/di/server.go b/internal/di/server.go index 6fa8521..0f2cba4 100644 --- a/internal/di/server.go +++ b/internal/di/server.go @@ -15,7 +15,7 @@ import ( "ely.by/chrly/internal/security" ) -var server = di.Options( +var serverDiOptions = di.Options( di.Provide(newAuthenticator, di.As(new(Authenticator))), di.Provide(newServer), ) diff --git a/internal/dispatcher/dispatcher.go b/internal/dispatcher/dispatcher.go deleted file mode 100644 index 4c692cd..0000000 --- a/internal/dispatcher/dispatcher.go +++ /dev/null @@ -1,34 +0,0 @@ -package dispatcher - -import "github.com/asaskevich/EventBus" - -type Subscriber interface { - Subscribe(topic string, fn interface{}) -} - -type Emitter interface { - Emit(topic string, args ...interface{}) -} - -type Dispatcher interface { - Subscriber - Emitter -} - -type localEventDispatcher struct { - bus EventBus.Bus -} - -func (d *localEventDispatcher) Subscribe(topic string, fn interface{}) { - _ = d.bus.Subscribe(topic, fn) -} - -func (d *localEventDispatcher) Emit(topic string, args ...interface{}) { - d.bus.Publish(topic, args...) -} - -func New() Dispatcher { - return &localEventDispatcher{ - bus: EventBus.New(), - } -} diff --git a/internal/eventsubscribers/health_checkers.go b/internal/eventsubscribers/health_checkers.go deleted file mode 100644 index 04cba23..0000000 --- a/internal/eventsubscribers/health_checkers.go +++ /dev/null @@ -1,60 +0,0 @@ -package eventsubscribers - -import ( - "context" - "errors" - "sync" - "time" - - "github.com/etherlabsio/healthcheck/v2" -) - -type Pingable interface { - Ping() error -} - -func DatabaseChecker(connection Pingable) healthcheck.CheckerFunc { - return func(ctx context.Context) error { - done := make(chan error) - go func() { - done <- connection.Ping() - }() - - select { - case <-ctx.Done(): - return errors.New("check timeout") - case err := <-done: - return err - } - } -} - -type expiringErrHolder struct { - D time.Duration - err error - l sync.Mutex - t *time.Timer -} - -func (h *expiringErrHolder) Get() error { - h.l.Lock() - defer h.l.Unlock() - - return h.err -} - -func (h *expiringErrHolder) Set(err error) { - h.l.Lock() - defer h.l.Unlock() - if h.t != nil { - h.t.Stop() - h.t = nil - } - - h.err = err - if err != nil { - h.t = time.AfterFunc(h.D, func() { - h.Set(nil) - }) - } -} diff --git a/internal/eventsubscribers/health_checkers_test.go b/internal/eventsubscribers/health_checkers_test.go deleted file mode 100644 index c873059..0000000 --- a/internal/eventsubscribers/health_checkers_test.go +++ /dev/null @@ -1,50 +0,0 @@ -package eventsubscribers - -import ( - "context" - "errors" - "testing" - "time" - - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/mock" -) - -type pingableMock struct { - mock.Mock -} - -func (p *pingableMock) Ping() error { - args := p.Called() - return args.Error(0) -} - -func TestDatabaseChecker(t *testing.T) { - t.Run("no error", func(t *testing.T) { - p := &pingableMock{} - p.On("Ping").Return(nil) - checker := DatabaseChecker(p) - assert.Nil(t, checker(context.Background())) - }) - - t.Run("with error", func(t *testing.T) { - err := errors.New("mock error") - p := &pingableMock{} - p.On("Ping").Return(err) - checker := DatabaseChecker(p) - assert.Equal(t, err, checker(context.Background())) - }) - - t.Run("context timeout", func(t *testing.T) { - p := &pingableMock{} - waitChan := make(chan time.Time, 1) - p.On("Ping").WaitUntil(waitChan).Return(nil) - - ctx, cancel := context.WithTimeout(context.Background(), 0) - defer cancel() - - checker := DatabaseChecker(p) - assert.Errorf(t, checker(ctx), "check timeout") - close(waitChan) - }) -} diff --git a/internal/eventsubscribers/logger.go b/internal/eventsubscribers/logger.go deleted file mode 100644 index 319e35d..0000000 --- a/internal/eventsubscribers/logger.go +++ /dev/null @@ -1,41 +0,0 @@ -package eventsubscribers - -import ( - "net/http" - "strings" - - "github.com/mono83/slf" - "github.com/mono83/slf/wd" -) - -type Logger struct { - slf.Logger -} - -func (l *Logger) ConfigureWithDispatcher(d Subscriber) { - d.Subscribe("skinsystem:after_request", l.handleAfterSkinsystemRequest) -} - -func (l *Logger) handleAfterSkinsystemRequest(req *http.Request, statusCode int) { - path := req.URL.Path - if req.URL.RawQuery != "" { - path += "?" + req.URL.RawQuery - } - - l.Info( - ":ip - - \":method :path\" :statusCode - \":userAgent\" \":forwardedIp\"", - wd.StringParam("ip", trimPort(req.RemoteAddr)), - wd.StringParam("method", req.Method), - wd.StringParam("path", path), - wd.IntParam("statusCode", statusCode), - wd.StringParam("userAgent", req.UserAgent()), - wd.StringParam("forwardedIp", req.Header.Get("X-Forwarded-For")), - ) -} - -func trimPort(ip string) string { - // Don't care about possible -1 result because RemoteAddr will always contain ip and port - cutTo := strings.LastIndexByte(ip, ':') - - return ip[0:cutTo] -} diff --git a/internal/eventsubscribers/logger_test.go b/internal/eventsubscribers/logger_test.go deleted file mode 100644 index 3869f38..0000000 --- a/internal/eventsubscribers/logger_test.go +++ /dev/null @@ -1,159 +0,0 @@ -package eventsubscribers - -import ( - "net/http" - "net/http/httptest" - "testing" - - "github.com/mono83/slf" - "github.com/mono83/slf/params" - "github.com/stretchr/testify/mock" - - "ely.by/chrly/internal/dispatcher" -) - -type LoggerMock struct { - mock.Mock -} - -func prepareLoggerArgs(message string, params []slf.Param) []interface{} { - args := []interface{}{message} - for _, v := range params { - args = append(args, v.(interface{})) - } - - return args -} - -func (l *LoggerMock) Trace(message string, params ...slf.Param) { - l.Called(prepareLoggerArgs(message, params)...) -} - -func (l *LoggerMock) Debug(message string, params ...slf.Param) { - l.Called(prepareLoggerArgs(message, params)...) -} - -func (l *LoggerMock) Info(message string, params ...slf.Param) { - l.Called(prepareLoggerArgs(message, params)...) -} - -func (l *LoggerMock) Warning(message string, params ...slf.Param) { - l.Called(prepareLoggerArgs(message, params)...) -} - -func (l *LoggerMock) Error(message string, params ...slf.Param) { - l.Called(prepareLoggerArgs(message, params)...) -} - -func (l *LoggerMock) Alert(message string, params ...slf.Param) { - l.Called(prepareLoggerArgs(message, params)...) -} - -func (l *LoggerMock) Emergency(message string, params ...slf.Param) { - l.Called(prepareLoggerArgs(message, params)...) -} - -type LoggerTestCase struct { - Events [][]interface{} - ExpectedCalls [][]interface{} -} - -var loggerTestCases = map[string]*LoggerTestCase{ - "should log each request to the skinsystem": { - Events: [][]interface{}{ - {"skinsystem:after_request", - (func() *http.Request { - req := httptest.NewRequest("GET", "http://localhost/skins/username.png", nil) - req.Header.Add("User-Agent", "Test user agent") - - return req - })(), - 201, - }, - }, - ExpectedCalls: [][]interface{}{ - {"Info", - ":ip - - \":method :path\" :statusCode - \":userAgent\" \":forwardedIp\"", - mock.MatchedBy(func(strParam params.String) bool { - return strParam.Key == "ip" && strParam.Value == "192.0.2.1" - }), - mock.MatchedBy(func(strParam params.String) bool { - return strParam.Key == "method" && strParam.Value == "GET" - }), - mock.MatchedBy(func(strParam params.String) bool { - return strParam.Key == "path" && strParam.Value == "/skins/username.png" - }), - mock.MatchedBy(func(strParam params.Int) bool { - return strParam.Key == "statusCode" && strParam.Value == 201 - }), - mock.MatchedBy(func(strParam params.String) bool { - return strParam.Key == "userAgent" && strParam.Value == "Test user agent" - }), - mock.MatchedBy(func(strParam params.String) bool { - return strParam.Key == "forwardedIp" && strParam.Value == "" - }), - }, - }, - }, - "should log each request to the skinsystem 2": { - Events: [][]interface{}{ - {"skinsystem:after_request", - (func() *http.Request { - req := httptest.NewRequest("GET", "http://localhost/skins/username.png?authlib=1.5.2", nil) - req.Header.Add("User-Agent", "Test user agent") - req.Header.Add("X-Forwarded-For", "1.2.3.4") - - return req - })(), - 201, - }, - }, - ExpectedCalls: [][]interface{}{ - {"Info", - ":ip - - \":method :path\" :statusCode - \":userAgent\" \":forwardedIp\"", - mock.Anything, // Already tested - mock.Anything, // Already tested - mock.MatchedBy(func(strParam params.String) bool { - return strParam.Key == "path" && strParam.Value == "/skins/username.png?authlib=1.5.2" - }), - mock.Anything, // Already tested - mock.Anything, // Already tested - mock.MatchedBy(func(strParam params.String) bool { - return strParam.Key == "forwardedIp" && strParam.Value == "1.2.3.4" - }), - }, - }, - }, -} - -func TestLogger(t *testing.T) { - for name, c := range loggerTestCases { - t.Run(name, func(t *testing.T) { - loggerMock := &LoggerMock{} - if c.ExpectedCalls != nil { - for _, c := range c.ExpectedCalls { - topicName, _ := c[0].(string) - loggerMock.On(topicName, c[1:]...) - } - } - - reporter := &Logger{ - Logger: loggerMock, - } - - d := dispatcher.New() - reporter.ConfigureWithDispatcher(d) - for _, args := range c.Events { - eventName, _ := args[0].(string) - d.Emit(eventName, args[1:]...) - } - - if c.ExpectedCalls != nil { - for _, c := range c.ExpectedCalls { - topicName, _ := c[0].(string) - loggerMock.AssertCalled(t, topicName, c[1:]...) - } - } - }) - } -} diff --git a/internal/eventsubscribers/stats_reporter.go b/internal/eventsubscribers/stats_reporter.go deleted file mode 100644 index f911842..0000000 --- a/internal/eventsubscribers/stats_reporter.go +++ /dev/null @@ -1,116 +0,0 @@ -package eventsubscribers - -import ( - "net/http" - "strings" - "sync" - "time" - - "github.com/mono83/slf" -) - -type StatsReporter struct { - slf.StatsReporter - Prefix string - - timersMap map[string]time.Time - timersMutex sync.Mutex -} - -type Reporter interface { - Enable(reporter slf.StatsReporter) -} - -type ReporterFunc func(reporter slf.StatsReporter) - -func (f ReporterFunc) Enable(reporter slf.StatsReporter) { - f(reporter) -} - -// TODO: rework all reporters in the same style as it was there: https://github.com/elyby/chrly/blob/1543e98b/di/db.go#L48-L52 -func (s *StatsReporter) ConfigureWithDispatcher(d Subscriber) { - s.timersMap = make(map[string]time.Time) - - // Per request events - d.Subscribe("skinsystem:before_request", s.handleBeforeRequest) - d.Subscribe("skinsystem:after_request", s.handleAfterRequest) - - // Authentication events - d.Subscribe("authenticator:success", s.incCounterHandler("authentication.challenge")) // TODO: legacy, remove in v5 - d.Subscribe("authenticator:success", s.incCounterHandler("authentication.success")) - d.Subscribe("authentication:error", s.incCounterHandler("authentication.challenge")) // TODO: legacy, remove in v5 - d.Subscribe("authentication:error", s.incCounterHandler("authentication.failed")) -} - -func (s *StatsReporter) handleBeforeRequest(req *http.Request) { - var key string - m := req.Method - p := req.URL.Path - if p == "/skins" { - key = "skins.get_request" - } else if strings.HasPrefix(p, "/skins/") { - key = "skins.request" - } else if p == "/cloaks" { - key = "capes.get_request" - } else if strings.HasPrefix(p, "/cloaks/") { - key = "capes.request" - } else if strings.HasPrefix(p, "/textures/signed/") { - key = "signed_textures.request" - } else if strings.HasPrefix(p, "/textures/") { - key = "textures.request" - } else if strings.HasPrefix(p, "/profile/") { - key = "profiles.request" - } else if m == http.MethodPost && p == "/api/skins" { - key = "api.skins.post.request" - } else if m == http.MethodDelete && strings.HasPrefix(p, "/api/skins/") { - key = "api.skins.delete.request" - } else { - return - } - - s.IncCounter(key, 1) -} - -func (s *StatsReporter) handleAfterRequest(req *http.Request, code int) { - var key string - m := req.Method - p := req.URL.Path - if m == http.MethodPost && p == "/api/skins" && code == http.StatusCreated { - key = "api.skins.post.success" - } else if m == http.MethodPost && p == "/api/skins" && code == http.StatusBadRequest { - key = "api.skins.post.validation_failed" - } else if m == http.MethodDelete && strings.HasPrefix(p, "/api/skins/") && code == http.StatusNoContent { - key = "api.skins.delete.success" - } else if m == http.MethodDelete && strings.HasPrefix(p, "/api/skins/") && code == http.StatusNotFound { - key = "api.skins.delete.not_found" - } else { - return - } - - s.IncCounter(key, 1) -} - -func (s *StatsReporter) incCounterHandler(name string) func(...interface{}) { - return func(...interface{}) { - s.IncCounter(name, 1) - } -} - -func (s *StatsReporter) startTimeRecording(timeKey string) { - s.timersMutex.Lock() - defer s.timersMutex.Unlock() - s.timersMap[timeKey] = time.Now() -} - -func (s *StatsReporter) finalizeTimeRecording(timeKey string, statName string) { - s.timersMutex.Lock() - defer s.timersMutex.Unlock() - startedAt, ok := s.timersMap[timeKey] - if !ok { - return - } - - delete(s.timersMap, timeKey) - - s.RecordTimer(statName, time.Since(startedAt)) -} diff --git a/internal/eventsubscribers/stats_reporter_test.go b/internal/eventsubscribers/stats_reporter_test.go deleted file mode 100644 index 2b8d08e..0000000 --- a/internal/eventsubscribers/stats_reporter_test.go +++ /dev/null @@ -1,240 +0,0 @@ -package eventsubscribers - -import ( - "errors" - "net/http/httptest" - "testing" - "time" - - "github.com/mono83/slf" - - "ely.by/chrly/internal/dispatcher" - - "github.com/stretchr/testify/mock" -) - -func prepareStatsReporterArgs(name string, value interface{}, params []slf.Param) []interface{} { - args := []interface{}{name, value} - for _, v := range params { - args = append(args, v.(interface{})) - } - - return args -} - -type StatsReporterMock struct { - mock.Mock -} - -func (r *StatsReporterMock) IncCounter(name string, value int64, params ...slf.Param) { - r.Called(prepareStatsReporterArgs(name, value, params)...) -} - -func (r *StatsReporterMock) UpdateGauge(name string, value int64, params ...slf.Param) { - r.Called(prepareStatsReporterArgs(name, value, params)...) -} - -func (r *StatsReporterMock) RecordTimer(name string, duration time.Duration, params ...slf.Param) { - r.Called(prepareStatsReporterArgs(name, duration, params)...) -} - -func (r *StatsReporterMock) Timer(name string, params ...slf.Param) slf.Timer { - return slf.NewTimer(name, params, r) -} - -type StatsReporterTestCase struct { - Events [][]interface{} - ExpectedCalls [][]interface{} -} - -var statsReporterTestCases = []*StatsReporterTestCase{ - // Before request - { - Events: [][]interface{}{ - {"skinsystem:before_request", httptest.NewRequest("GET", "http://localhost/skins/username", nil)}, - }, - ExpectedCalls: [][]interface{}{ - {"IncCounter", "skins.request", int64(1)}, - }, - }, - { - Events: [][]interface{}{ - {"skinsystem:before_request", httptest.NewRequest("GET", "http://localhost/skins?name=username", nil)}, - }, - ExpectedCalls: [][]interface{}{ - {"IncCounter", "skins.get_request", int64(1)}, - }, - }, - { - Events: [][]interface{}{ - {"skinsystem:before_request", httptest.NewRequest("GET", "http://localhost/cloaks/username", nil)}, - }, - ExpectedCalls: [][]interface{}{ - {"IncCounter", "capes.request", int64(1)}, - }, - }, - { - Events: [][]interface{}{ - {"skinsystem:before_request", httptest.NewRequest("GET", "http://localhost/cloaks?name=username", nil)}, - }, - ExpectedCalls: [][]interface{}{ - {"IncCounter", "capes.get_request", int64(1)}, - }, - }, - { - Events: [][]interface{}{ - {"skinsystem:before_request", httptest.NewRequest("GET", "http://localhost/textures/username", nil)}, - }, - ExpectedCalls: [][]interface{}{ - {"IncCounter", "textures.request", int64(1)}, - }, - }, - { - Events: [][]interface{}{ - {"skinsystem:before_request", httptest.NewRequest("GET", "http://localhost/textures/signed/username", nil)}, - }, - ExpectedCalls: [][]interface{}{ - {"IncCounter", "signed_textures.request", int64(1)}, - }, - }, - { - Events: [][]interface{}{ - {"skinsystem:before_request", httptest.NewRequest("GET", "http://localhost/profile/username", nil)}, - }, - ExpectedCalls: [][]interface{}{ - {"IncCounter", "profiles.request", int64(1)}, - }, - }, - { - Events: [][]interface{}{ - {"skinsystem:before_request", httptest.NewRequest("POST", "http://localhost/api/skins", nil)}, - }, - ExpectedCalls: [][]interface{}{ - {"IncCounter", "api.skins.post.request", int64(1)}, - }, - }, - { - Events: [][]interface{}{ - {"skinsystem:before_request", httptest.NewRequest("DELETE", "http://localhost/api/skins/username", nil)}, - }, - ExpectedCalls: [][]interface{}{ - {"IncCounter", "api.skins.delete.request", int64(1)}, - }, - }, - { - Events: [][]interface{}{ - {"skinsystem:before_request", httptest.NewRequest("DELETE", "http://localhost/api/skins/id:1", nil)}, - }, - ExpectedCalls: [][]interface{}{ - {"IncCounter", "api.skins.delete.request", int64(1)}, - }, - }, - { - Events: [][]interface{}{ - {"skinsystem:before_request", httptest.NewRequest("GET", "http://localhost/unknown", nil)}, - }, - ExpectedCalls: nil, - }, - // After request - { - Events: [][]interface{}{ - {"skinsystem:after_request", httptest.NewRequest("POST", "http://localhost/api/skins", nil), 201}, - }, - ExpectedCalls: [][]interface{}{ - {"IncCounter", "api.skins.post.success", int64(1)}, - }, - }, - { - Events: [][]interface{}{ - {"skinsystem:after_request", httptest.NewRequest("POST", "http://localhost/api/skins", nil), 400}, - }, - ExpectedCalls: [][]interface{}{ - {"IncCounter", "api.skins.post.validation_failed", int64(1)}, - }, - }, - { - Events: [][]interface{}{ - {"skinsystem:after_request", httptest.NewRequest("DELETE", "http://localhost/api/skins/username", nil), 204}, - }, - ExpectedCalls: [][]interface{}{ - {"IncCounter", "api.skins.delete.success", int64(1)}, - }, - }, - { - Events: [][]interface{}{ - {"skinsystem:after_request", httptest.NewRequest("DELETE", "http://localhost/api/skins/username", nil), 404}, - }, - ExpectedCalls: [][]interface{}{ - {"IncCounter", "api.skins.delete.not_found", int64(1)}, - }, - }, - { - Events: [][]interface{}{ - {"skinsystem:after_request", httptest.NewRequest("DELETE", "http://localhost/api/skins/id:1", nil), 204}, - }, - ExpectedCalls: [][]interface{}{ - {"IncCounter", "api.skins.delete.success", int64(1)}, - }, - }, - { - Events: [][]interface{}{ - {"skinsystem:after_request", httptest.NewRequest("DELETE", "http://localhost/api/skins/id:1", nil), 404}, - }, - ExpectedCalls: [][]interface{}{ - {"IncCounter", "api.skins.delete.not_found", int64(1)}, - }, - }, - { - Events: [][]interface{}{ - {"skinsystem:after_request", httptest.NewRequest("DELETE", "http://localhost/unknown", nil), 404}, - }, - ExpectedCalls: nil, - }, - // Authenticator - { - Events: [][]interface{}{ - {"authenticator:success"}, - }, - ExpectedCalls: [][]interface{}{ - {"IncCounter", "authentication.challenge", int64(1)}, - {"IncCounter", "authentication.success", int64(1)}, - }, - }, - { - Events: [][]interface{}{ - {"authentication:error", errors.New("error")}, - }, - ExpectedCalls: [][]interface{}{ - {"IncCounter", "authentication.challenge", int64(1)}, - {"IncCounter", "authentication.failed", int64(1)}, - }, - }, -} - -func TestStatsReporter(t *testing.T) { - for _, c := range statsReporterTestCases { - t.Run("handle events", func(t *testing.T) { - statsReporterMock := &StatsReporterMock{} - if c.ExpectedCalls != nil { - for _, c := range c.ExpectedCalls { - topicName, _ := c[0].(string) - statsReporterMock.On(topicName, c[1:]...).Once() - } - } - - reporter := &StatsReporter{ - StatsReporter: statsReporterMock, - Prefix: "mock_prefix", - } - - d := dispatcher.New() - reporter.ConfigureWithDispatcher(d) - for _, e := range c.Events { - eventName, _ := e[0].(string) - d.Emit(eventName, e[1:]...) - } - - statsReporterMock.AssertExpectations(t) - }) - } -} diff --git a/internal/eventsubscribers/subscriber.go b/internal/eventsubscribers/subscriber.go deleted file mode 100644 index f4897d4..0000000 --- a/internal/eventsubscribers/subscriber.go +++ /dev/null @@ -1,7 +0,0 @@ -package eventsubscribers - -import "ely.by/chrly/internal/dispatcher" - -type Subscriber interface { - dispatcher.Subscriber -} diff --git a/internal/http/http.go b/internal/http/http.go index 2e86de3..25e30b1 100644 --- a/internal/http/http.go +++ b/internal/http/http.go @@ -7,23 +7,17 @@ import ( "net/http" "os" "os/signal" - "strings" "syscall" "github.com/gorilla/mux" "github.com/mono83/slf" "github.com/mono83/slf/wd" - "ely.by/chrly/internal/dispatcher" - v "ely.by/chrly/internal/version" + "ely.by/chrly/internal/version" ) -type Emitter interface { - dispatcher.Emitter -} - func StartServer(server *http.Server, logger slf.Logger) { - logger.Debug("Chrly :v (:c)", wd.StringParam("v", v.Version()), wd.StringParam("c", v.Commit())) + logger.Debug("Chrly :v (:c)", wd.StringParam("v", version.Version()), wd.StringParam("c", version.Commit())) done := make(chan bool, 1) go func() { @@ -62,21 +56,14 @@ func (lrw *loggingResponseWriter) WriteHeader(code int) { lrw.ResponseWriter.WriteHeader(code) } -func CreateRequestEventsMiddleware(emitter Emitter, prefix string) mux.MiddlewareFunc { - beforeTopic := strings.Join([]string{prefix, "before_request"}, ":") - afterTopic := strings.Join([]string{prefix, "after_request"}, ":") - +func CreateRequestEventsMiddleware() mux.MiddlewareFunc { return func(handler http.Handler) http.Handler { return http.HandlerFunc(func(resp http.ResponseWriter, req *http.Request) { - emitter.Emit(beforeTopic, req) - lrw := &loggingResponseWriter{ ResponseWriter: resp, statusCode: http.StatusOK, } handler.ServeHTTP(lrw, req) - - emitter.Emit(afterTopic, req, lrw.statusCode) }) } } diff --git a/internal/http/http_test.go b/internal/http/http_test.go index ab9a63d..1634e3b 100644 --- a/internal/http/http_test.go +++ b/internal/http/http_test.go @@ -11,24 +11,12 @@ import ( "github.com/stretchr/testify/mock" ) -type emitterMock struct { - mock.Mock -} - -func (e *emitterMock) Emit(name string, args ...interface{}) { - e.Called(append([]interface{}{name}, args...)...) -} - func TestCreateRequestEventsMiddleware(t *testing.T) { req := httptest.NewRequest("GET", "http://example.com", nil) resp := httptest.NewRecorder() - emitter := &emitterMock{} - emitter.On("Emit", "test_prefix:before_request", req) - emitter.On("Emit", "test_prefix:after_request", req, 400) - isHandlerCalled := false - middlewareFunc := CreateRequestEventsMiddleware(emitter, "test_prefix") + middlewareFunc := CreateRequestEventsMiddleware() middlewareFunc.Middleware(http.HandlerFunc(func(resp http.ResponseWriter, req *http.Request) { resp.WriteHeader(400) isHandlerCalled = true @@ -37,8 +25,6 @@ func TestCreateRequestEventsMiddleware(t *testing.T) { if !isHandlerCalled { t.Fatal("Handler isn't called from the middleware") } - - emitter.AssertExpectations(t) } type authCheckerMock struct {