From ad59438a966e00c3c6ca9ac341c4a52f7f351f8d Mon Sep 17 00:00:00 2001 From: darkweak Date: Sat, 19 Aug 2023 22:39:09 +0200 Subject: [PATCH] feat(chore): allow chained storages --- configurationtypes/types.go | 7 ++ pkg/api/main.go | 8 +-- pkg/api/souin.go | 29 ++++++--- pkg/middleware/middleware.go | 65 ++++++++++++++++--- pkg/storage/badgerProvider.go | 5 ++ pkg/storage/embeddedOlricProvider.go | 5 ++ pkg/storage/etcdProvider.go | 5 ++ pkg/storage/nutsProvider.go | 5 ++ pkg/storage/olricProvider.go | 5 ++ pkg/storage/redisProvider.go | 5 ++ pkg/storage/storage.go | 50 ++++++++++++++ plugins/beego/souin_test.go | 2 +- plugins/caddy/configuration.go | 10 +++ plugins/caddy/httpcache.go | 6 ++ plugins/chi/souin_test.go | 2 +- plugins/dotweb/souin_test.go | 2 +- plugins/echo/souin_test.go | 2 +- plugins/fiber/souin_test.go | 2 +- plugins/gin/souin_test.go | 2 +- plugins/go-zero/souin_test.go | 2 +- plugins/goa/souin_test.go | 2 +- plugins/goyave/souin_test.go | 2 +- plugins/kratos/configuration.go | 7 ++ .../souin/agnostic/configuration_parser.go | 12 ++++ plugins/traefik/main.go | 2 + .../souin/configurationtypes/types.go | 7 ++ plugins/webgo/souin_test.go | 2 +- 27 files changed, 221 insertions(+), 32 deletions(-) diff --git a/configurationtypes/types.go b/configurationtypes/types.go index 53aac6bfe..609f14103 100644 --- a/configurationtypes/types.go +++ b/configurationtypes/types.go @@ -229,6 +229,7 @@ type DefaultCache struct { Port Port `json:"port" yaml:"port"` Regex Regex `json:"regex" yaml:"regex"` Stale Duration `json:"stale" yaml:"stale"` + Storers []string `json:"storers" yaml:"storers"` Timeout Timeout `json:"timeout" yaml:"timeout"` TTL Duration `json:"ttl" yaml:"ttl"` DefaultCacheControl string `json:"default_cache_control" yaml:"default_cache_control"` @@ -314,6 +315,11 @@ func (d *DefaultCache) GetStale() time.Duration { return d.Stale.Duration } +// GetStale returns the stale duration +func (d *DefaultCache) GetStorers() []string { + return d.Storers +} + // GetDefaultCacheControl returns the default Cache-Control response header value when empty func (d *DefaultCache) GetDefaultCacheControl() string { return d.DefaultCacheControl @@ -335,6 +341,7 @@ type DefaultCacheInterface interface { GetKey() Key GetRegex() Regex GetStale() time.Duration + GetStorers() []string GetTimeout() Timeout GetTTL() time.Duration GetDefaultCacheControl() string diff --git a/pkg/api/main.go b/pkg/api/main.go index 2da05d13c..bcde6d808 100644 --- a/pkg/api/main.go +++ b/pkg/api/main.go @@ -18,7 +18,7 @@ type MapHandler struct { // GenerateHandlerMap generate the MapHandler func GenerateHandlerMap( configuration configurationtypes.AbstractConfigurationInterface, - storer storage.Storer, + storers []storage.Storer, surrogateStorage providers.SurrogateInterface, ) *MapHandler { hm := make(map[string]http.HandlerFunc) @@ -30,7 +30,7 @@ func GenerateHandlerMap( basePathAPIS = "/souin-api" } - for _, endpoint := range Initialize(configuration, storer, surrogateStorage) { + for _, endpoint := range Initialize(configuration, storers, surrogateStorage) { if endpoint.IsEnabled() { shouldEnable = true hm[basePathAPIS+endpoint.GetBasePath()] = endpoint.HandleRequest @@ -45,7 +45,7 @@ func GenerateHandlerMap( } // Initialize contains all apis that should be enabled -func Initialize(c configurationtypes.AbstractConfigurationInterface, storer storage.Storer, surrogateStorage providers.SurrogateInterface) []EndpointInterface { - return []EndpointInterface{initializeSouin(c, storer, +func Initialize(c configurationtypes.AbstractConfigurationInterface, storers []storage.Storer, surrogateStorage providers.SurrogateInterface) []EndpointInterface { + return []EndpointInterface{initializeSouin(c, storers, surrogateStorage), debug.InitializeDebug(c), prometheus.InitializePrometheus(c)} } diff --git a/pkg/api/souin.go b/pkg/api/souin.go index 81c72f68d..da73bcbd4 100644 --- a/pkg/api/souin.go +++ b/pkg/api/souin.go @@ -15,13 +15,13 @@ import ( type SouinAPI struct { basePath string enabled bool - storer storage.Storer + storers []storage.Storer surrogateStorage providers.SurrogateInterface } func initializeSouin( configuration configurationtypes.AbstractConfigurationInterface, - storer storage.Storer, + storers []storage.Storer, surrogateStorage providers.SurrogateInterface, ) *SouinAPI { basePath := configuration.GetAPI().Souin.BasePath @@ -31,24 +31,33 @@ func initializeSouin( return &SouinAPI{ basePath, configuration.GetAPI().Souin.Enable, - storer, + storers, surrogateStorage, } } // BulkDelete allow user to delete multiple items with regexp func (s *SouinAPI) BulkDelete(key string) { - s.storer.DeleteMany(key) + for _, current := range s.storers { + current.DeleteMany(key) + } } // Delete will delete a record into the provider cache system and will update the Souin API if enabled func (s *SouinAPI) Delete(key string) { - s.storer.Delete(key) + for _, current := range s.storers { + current.Delete(key) + } } // GetAll will retrieve all stored keys in the provider func (s *SouinAPI) GetAll() []string { - return s.storer.ListKeys() + keys := []string{} + for _, current := range s.storers { + keys = append(keys, current.ListKeys()...) + } + + return keys } // GetBasePath will return the basepath for this resource @@ -100,7 +109,9 @@ func (s *SouinAPI) HandleRequest(w http.ResponseWriter, r *http.Request) { flushRg := regexp.MustCompile(s.GetBasePath() + "/flush$") if flushRg.FindString(r.RequestURI) != "" { - s.storer.DeleteMany(".+") + for _, current := range s.storers { + current.DeleteMany(".+") + } e := s.surrogateStorage.Destruct() if e != nil { fmt.Printf("Error while purging the surrogate keys: %+v.", e) @@ -113,7 +124,9 @@ func (s *SouinAPI) HandleRequest(w http.ResponseWriter, r *http.Request) { } else { ck, _ := s.surrogateStorage.Purge(r.Header) for _, k := range ck { - s.storer.Delete(k) + for _, current := range s.storers { + current.Delete(k) + } } } w.WriteHeader(http.StatusNoContent) diff --git a/pkg/middleware/middleware.go b/pkg/middleware/middleware.go index c30544104..1292625af 100644 --- a/pkg/middleware/middleware.go +++ b/pkg/middleware/middleware.go @@ -57,7 +57,7 @@ func NewHTTPCacheHandler(c configurationtypes.AbstractConfigurationInterface) *S c.SetLogger(logger) } - storer, err := storage.NewStorage(c) + storers, err := storage.NewStorages(c) if err != nil { panic(err) } @@ -87,20 +87,22 @@ func NewHTTPCacheHandler(c configurationtypes.AbstractConfigurationInterface) *S return &SouinBaseHandler{ Configuration: c, - Storer: storer, - InternalEndpointHandlers: api.GenerateHandlerMap(c, storer, surrogateStorage), + Storers: storers, + InternalEndpointHandlers: api.GenerateHandlerMap(c, storers, surrogateStorage), ExcludeRegex: excludedRegexp, RegexpUrls: regexpUrls, DefaultMatchedUrl: defaultMatchedUrl, SurrogateKeyStorer: surrogateStorage, context: ctx, bufPool: bufPool, + storersLen: len(storers), } } type SouinBaseHandler struct { Configuration configurationtypes.AbstractConfigurationInterface Storer storage.Storer + Storers []storage.Storer InternalEndpointHandlers *api.MapHandler ExcludeRegex *regexp.Regexp RegexpUrls regexp.Regexp @@ -109,6 +111,7 @@ type SouinBaseHandler struct { DefaultMatchedUrl configurationtypes.URL context *context.Context bufPool *sync.Pool + storersLen int } type upsreamError struct{} @@ -234,16 +237,46 @@ func (s *SouinBaseHandler) Store( if err == nil { variedHeaders := rfc.HeaderAllCommaSepValues(res.Header) cachedKey += rfc.GetVariedCacheKey(rq, variedHeaders) - s.Configuration.GetLogger().Sugar().Infof("Store the response %+v with duration %v", res, ma) - if s.Storer.Set(cachedKey, response, currentMatchedURL, ma) == nil { - s.Configuration.GetLogger().Sugar().Debugf("Store the cache key %s into the surrogate keys from the following headers %v", cachedKey, res) + s.Configuration.GetLogger().Sugar().Debugf("Store the response %+v with duration %v", res, ma) + + var wg sync.WaitGroup + mu := sync.Mutex{} + fails := []string{} + for _, storer := range s.Storers { + wg.Add(1) + go func(currentStorer storage.Storer) { + defer wg.Done() + if currentStorer.Set(cachedKey, response, currentMatchedURL, ma) == nil { + s.Configuration.GetLogger().Sugar().Infof("Stored the key %s in the %s provider", cachedKey, currentStorer.Name()) + } else { + mu.Lock() + fails = append(fails, fmt.Sprintf("; detail=%s-INSERTION-ERROR", currentStorer.Name())) + mu.Unlock() + } + }(storer) + } + + wg.Wait() + if len(fails) < s.storersLen { go func(rs http.Response, key string) { _ = s.SurrogateKeyStorer.Store(&rs, key) }(res, cachedKey) status += "; stored" - } else { - status += "; detail=STORAGE-INSERTION-ERROR" } + + if len(fails) > 0 { + status += strings.Join(fails, "") + } + + // if s.Storer.Set(cachedKey, response, currentMatchedURL, ma) == nil { + // s.Configuration.GetLogger().Sugar().Debugf("Store the cache key %s into the surrogate keys from the following headers %v", cachedKey, res) + // go func(rs http.Response, key string) { + // _ = s.SurrogateKeyStorer.Store(&rs, key) + // }(res, cachedKey) + // status += "; stored" + // } else { + // status += "; detail=STORAGE-INSERTION-ERROR" + // } } } else { status += "; detail=NO-STORE-DIRECTIVE" @@ -394,7 +427,14 @@ func (s *SouinBaseHandler) ServeHTTP(rw http.ResponseWriter, rq *http.Request, n s.Configuration.GetLogger().Sugar().Debugf("Request cache-control %+v", requestCc) if modeContext.Bypass_request || !requestCc.NoCache { validator := rfc.ParseRequest(rq) - response := s.Storer.Prefix(cachedKey, rq, validator) + var response *http.Response + for _, currentStorer := range s.Storers { + response = currentStorer.Prefix(cachedKey, rq, validator) + if response != nil { + s.Configuration.GetLogger().Sugar().Debugf("Found response in the %s storage", currentStorer.Name()) + break + } + } if response != nil && (!modeContext.Strict || rfc.ValidateCacheControl(response, requestCc)) { if validator.ResponseETag != "" && validator.Matched { @@ -425,7 +465,12 @@ func (s *SouinBaseHandler) ServeHTTP(rw http.ResponseWriter, rq *http.Request, n return err } } else if response == nil && !requestCc.OnlyIfCached && (requestCc.MaxStaleSet || requestCc.MaxStale > -1) { - response = s.Storer.Prefix(storage.StalePrefix+cachedKey, rq, validator) + for _, currentStorer := range s.Storers { + response := currentStorer.Prefix(storage.StalePrefix+cachedKey, rq, validator) + if response != nil { + break + } + } if nil != response && (!modeContext.Strict || rfc.ValidateCacheControl(response, requestCc)) { addTime, _ := time.ParseDuration(response.Header.Get(rfc.StoredTTLHeader)) rfc.SetCacheStatusHeader(response) diff --git a/pkg/storage/badgerProvider.go b/pkg/storage/badgerProvider.go index e1812de4b..077fb8b00 100644 --- a/pkg/storage/badgerProvider.go +++ b/pkg/storage/badgerProvider.go @@ -86,6 +86,11 @@ func BadgerConnectionFactory(c t.AbstractConfigurationInterface) (Storer, error) return i, nil } +// Name returns the storer name +func (provider *Badger) Name() string { + return "BADGER" +} + // ListKeys method returns the list of existing keys func (provider *Badger) ListKeys() []string { keys := []string{} diff --git a/pkg/storage/embeddedOlricProvider.go b/pkg/storage/embeddedOlricProvider.go index aae5831af..aa430f891 100644 --- a/pkg/storage/embeddedOlricProvider.go +++ b/pkg/storage/embeddedOlricProvider.go @@ -108,6 +108,11 @@ func EmbeddedOlricConnectionFactory(configuration t.AbstractConfigurationInterfa }, e } +// Name returns the storer name +func (provider *EmbeddedOlric) Name() string { + return "EMBEDDED_OLRIC" +} + // ListKeys method returns the list of existing keys func (provider *EmbeddedOlric) ListKeys() []string { diff --git a/pkg/storage/etcdProvider.go b/pkg/storage/etcdProvider.go index 6e09d5cf6..d08dfebfa 100644 --- a/pkg/storage/etcdProvider.go +++ b/pkg/storage/etcdProvider.go @@ -61,6 +61,11 @@ func EtcdConnectionFactory(c t.AbstractConfigurationInterface) (Storer, error) { }, nil } +// Name returns the storer name +func (provider *Etcd) Name() string { + return "ETCD" +} + // ListKeys method returns the list of existing keys func (provider *Etcd) ListKeys() []string { if provider.reconnecting { diff --git a/pkg/storage/nutsProvider.go b/pkg/storage/nutsProvider.go index 31349fc17..522adc053 100644 --- a/pkg/storage/nutsProvider.go +++ b/pkg/storage/nutsProvider.go @@ -106,6 +106,11 @@ func NutsConnectionFactory(c t.AbstractConfigurationInterface) (Storer, error) { }, nil } +// Name returns the storer name +func (provider *Nuts) Name() string { + return "NUTS" +} + // ListKeys method returns the list of existing keys func (provider *Nuts) ListKeys() []string { keys := []string{} diff --git a/pkg/storage/olricProvider.go b/pkg/storage/olricProvider.go index 14d313a8b..c9840417e 100644 --- a/pkg/storage/olricProvider.go +++ b/pkg/storage/olricProvider.go @@ -45,6 +45,11 @@ func OlricConnectionFactory(configuration t.AbstractConfigurationInterface) (Sto }, nil } +// Name returns the storer name +func (provider *Olric) Name() string { + return "OLRIC" +} + // ListKeys method returns the list of existing keys func (provider *Olric) ListKeys() []string { if provider.reconnecting { diff --git a/pkg/storage/redisProvider.go b/pkg/storage/redisProvider.go index 57863d66a..48891d9d0 100644 --- a/pkg/storage/redisProvider.go +++ b/pkg/storage/redisProvider.go @@ -57,6 +57,11 @@ func RedisConnectionFactory(c t.AbstractConfigurationInterface) (Storer, error) }, nil } +// Name returns the storer name +func (provider *Redis) Name() string { + return "REDIS" +} + // ListKeys method returns the list of existing keys func (provider *Redis) ListKeys() []string { if provider.reconnecting { diff --git a/pkg/storage/storage.go b/pkg/storage/storage.go index 484def9bd..13e696e0a 100644 --- a/pkg/storage/storage.go +++ b/pkg/storage/storage.go @@ -25,6 +25,7 @@ type Storer interface { Delete(key string) DeleteMany(key string) Init() error + Name() string Reset() error } @@ -67,6 +68,55 @@ func NewStorage(configuration configurationtypes.AbstractConfigurationInterface) return nil, errors.New("Storer with name" + storerName + " not found") } +func uniqueStorers(storers []string) []string { + storerPresent := make(map[string]bool) + s := []string{} + + for _, current := range storers { + if _, found := storerPresent[current]; !found { + storerPresent[current] = true + s = append(s, current) + } + } + + return s +} + +func NewStorages(configuration configurationtypes.AbstractConfigurationInterface) ([]Storer, error) { + storers := []Storer{} + for _, storerName := range uniqueStorers(configuration.GetDefaultCache().GetStorers()) { + if newStorage, found := storageMap[storerName]; found { + instance, err := newStorage(configuration) + if err != nil { + configuration.GetLogger().Sugar().Debugf("Cannot load configuration for the chianed provider %s: %+v", storerName, err) + continue + } + + configuration.GetLogger().Sugar().Debugf("Append storer %s to the chain", storerName) + storers = append(storers, instance) + } else { + configuration.GetLogger().Sugar().Debugf("Storer with name %s not found", storerName) + } + } + + if len(storers) == 0 { + configuration.GetLogger().Debug("Not able to create storers chain from the storers slice, fallback to the default storer creation") + instance, err := NewStorage(configuration) + if err != nil || instance == nil { + return nil, err + } + + storers = append(storers, instance) + } + + names := []string{} + for _, s := range storers { + names = append(names, s.Name()) + } + configuration.GetLogger().Sugar().Debugf("Run with %d chained providers with the given order %s", len(storers), strings.Join(names, ", ")) + return storers, nil +} + func varyVoter(baseKey string, req *http.Request, currentKey string) bool { if currentKey == baseKey { return true diff --git a/plugins/beego/souin_test.go b/plugins/beego/souin_test.go index 5c6bdac89..31429961f 100644 --- a/plugins/beego/souin_test.go +++ b/plugins/beego/souin_test.go @@ -17,7 +17,7 @@ import ( func Test_NewHTTPCache(t *testing.T) { s := NewHTTPCache(DevDefaultConfiguration) - if s.SouinBaseHandler.Storer == nil { + if s.SouinBaseHandler.Storers == nil || len(s.SouinBaseHandler.Storers) != 1 { t.Error("The storer must be set.") } c := middleware.BaseConfiguration{} diff --git a/plugins/caddy/configuration.go b/plugins/caddy/configuration.go index 93c0f066f..92371ee39 100644 --- a/plugins/caddy/configuration.go +++ b/plugins/caddy/configuration.go @@ -40,6 +40,8 @@ type DefaultCache struct { Nuts configurationtypes.CacheProvider `json:"nuts"` // Regex to exclude cache. Regex configurationtypes.Regex `json:"regex"` + // Storage providers chaining and order. + Storers []string `json:"storers"` // Time before cache or backend access timeout. Timeout configurationtypes.Timeout `json:"timeout"` // Time to live. @@ -113,6 +115,11 @@ func (d *DefaultCache) GetRegex() configurationtypes.Regex { return d.Regex } +// GetStorers returns the chianed storers +func (d *DefaultCache) GetStorers() []string { + return d.Storers +} + // GetTimeout returns the backend and cache timeouts func (d *DefaultCache) GetTimeout() configurationtypes.Timeout { return d.Timeout @@ -513,6 +520,9 @@ func parseConfiguration(cfg *Configuration, h *caddyfile.Dispenser, isBlocking b if err == nil { cfg.DefaultCache.Stale.Duration = stale } + case "storers": + args := h.RemainingArgs() + cfg.DefaultCache.Storers = args case "timeout": timeout := configurationtypes.Timeout{} for nesting := h.Nesting(); h.NextBlock(nesting); { diff --git a/plugins/caddy/httpcache.go b/plugins/caddy/httpcache.go index c5db767d7..80d59e8b0 100644 --- a/plugins/caddy/httpcache.go +++ b/plugins/caddy/httpcache.go @@ -63,6 +63,8 @@ type SouinCaddyMiddleware struct { TTL configurationtypes.Duration `json:"ttl,omitempty"` // Time to live for a stale key, using time.duration. Stale configurationtypes.Duration `json:"stale,omitempty"` + // Storage providers chaining and order. + Storers []string `json:"storers,omitempty"` // The default Cache-Control header value if none set by the upstream server. DefaultCacheControl string `json:"default_cache_control,omitempty"` // The cache name to use in the Cache-Status response header. @@ -102,6 +104,7 @@ func (s *SouinCaddyMiddleware) configurationPropertyMapper() error { Timeout: s.Timeout, TTL: s.TTL, Stale: s.Stale, + Storers: s.Storers, } if s.Configuration == nil { s.Configuration = &Configuration{ @@ -173,6 +176,9 @@ func (s *SouinCaddyMiddleware) FromApp(app *SouinApp) error { if dc.Stale.Duration == 0 { s.Configuration.DefaultCache.Stale = appDc.Stale } + if len(dc.Storers) == 0 { + s.Configuration.DefaultCache.Storers = appDc.Storers + } if dc.Timeout.Backend.Duration == 0 { s.Configuration.DefaultCache.Timeout.Backend = appDc.Timeout.Backend } diff --git a/plugins/chi/souin_test.go b/plugins/chi/souin_test.go index 30caebb01..ec5f11ddc 100644 --- a/plugins/chi/souin_test.go +++ b/plugins/chi/souin_test.go @@ -14,7 +14,7 @@ import ( func Test_NewHTTPCache(t *testing.T) { s := NewHTTPCache(DevDefaultConfiguration) - if s.SouinBaseHandler.Storer == nil { + if s.SouinBaseHandler.Storers == nil || len(s.SouinBaseHandler.Storers) != 1 { t.Error("The storer must be set.") } c := middleware.BaseConfiguration{} diff --git a/plugins/dotweb/souin_test.go b/plugins/dotweb/souin_test.go index 0bb20c327..1fc07a724 100644 --- a/plugins/dotweb/souin_test.go +++ b/plugins/dotweb/souin_test.go @@ -14,7 +14,7 @@ import ( func Test_NewHTTPCache(t *testing.T) { s := NewHTTPCache(DevDefaultConfiguration) - if s.SouinBaseHandler.Storer == nil { + if s.SouinBaseHandler.Storers == nil || len(s.SouinBaseHandler.Storers) != 1 { t.Error("The storer must be set.") } c := middleware.BaseConfiguration{} diff --git a/plugins/echo/souin_test.go b/plugins/echo/souin_test.go index 6df253ede..8506489ac 100644 --- a/plugins/echo/souin_test.go +++ b/plugins/echo/souin_test.go @@ -15,7 +15,7 @@ import ( func Test_New(t *testing.T) { s := NewMiddleware(DevDefaultConfiguration) - if s.SouinBaseHandler.Storer == nil { + if s.SouinBaseHandler.Storers == nil || len(s.SouinBaseHandler.Storers) != 1 { t.Error("The storer must be set.") } c := middleware.BaseConfiguration{} diff --git a/plugins/fiber/souin_test.go b/plugins/fiber/souin_test.go index 894b774df..cbf0bacf6 100644 --- a/plugins/fiber/souin_test.go +++ b/plugins/fiber/souin_test.go @@ -14,7 +14,7 @@ import ( func Test_NewHTTPCache(t *testing.T) { s := NewHTTPCache(DevDefaultConfiguration) - if s.SouinBaseHandler.Storer == nil { + if s.SouinBaseHandler.Storers == nil || len(s.SouinBaseHandler.Storers) != 1 { t.Error("The storer must be set.") } c := middleware.BaseConfiguration{} diff --git a/plugins/gin/souin_test.go b/plugins/gin/souin_test.go index a293a1758..6343f3457 100644 --- a/plugins/gin/souin_test.go +++ b/plugins/gin/souin_test.go @@ -11,7 +11,7 @@ import ( func Test_New(t *testing.T) { s := New(DevDefaultConfiguration) - if s.SouinBaseHandler.Storer == nil { + if s.SouinBaseHandler.Storers == nil || len(s.SouinBaseHandler.Storers) != 1 { t.Error("The storer must be set.") } c := middleware.BaseConfiguration{} diff --git a/plugins/go-zero/souin_test.go b/plugins/go-zero/souin_test.go index f50a6cf06..7e28e4ae6 100644 --- a/plugins/go-zero/souin_test.go +++ b/plugins/go-zero/souin_test.go @@ -13,7 +13,7 @@ import ( func Test_NewHTTPCache(t *testing.T) { s := NewHTTPCache(DevDefaultConfiguration) - if s.SouinBaseHandler.Storer == nil { + if s.SouinBaseHandler.Storers == nil || len(s.SouinBaseHandler.Storers) != 1 { t.Error("The storer must be set.") } c := middleware.BaseConfiguration{} diff --git a/plugins/goa/souin_test.go b/plugins/goa/souin_test.go index d1eefbd08..9100f26ea 100644 --- a/plugins/goa/souin_test.go +++ b/plugins/goa/souin_test.go @@ -16,7 +16,7 @@ func Test_NewHTTPCache(t *testing.T) { s := &SouinGoaMiddleware{ SouinBaseHandler: middleware.NewHTTPCacheHandler(&DevDefaultConfiguration), } - if s.SouinBaseHandler.Storer == nil { + if s.SouinBaseHandler.Storers == nil || len(s.SouinBaseHandler.Storers) != 1 { t.Error("The storer must be set.") } defer func() { diff --git a/plugins/goyave/souin_test.go b/plugins/goyave/souin_test.go index 5f57ba4cc..3787f364c 100644 --- a/plugins/goyave/souin_test.go +++ b/plugins/goyave/souin_test.go @@ -15,7 +15,7 @@ import ( func Test_NewHTTPCache(t *testing.T) { s := NewHTTPCache(DevDefaultConfiguration) - if s.SouinBaseHandler.Storer == nil { + if s.SouinBaseHandler.Storers == nil || len(s.SouinBaseHandler.Storers) != 1 { t.Error("The storer must be set.") } c := middleware.BaseConfiguration{} diff --git a/plugins/kratos/configuration.go b/plugins/kratos/configuration.go index bc6f40aec..ea46eca6e 100644 --- a/plugins/kratos/configuration.go +++ b/plugins/kratos/configuration.go @@ -302,6 +302,13 @@ func parseDefaultCache(dcConfiguration map[string]config.Value) *configurationty if err == nil { dc.Stale = configurationtypes.Duration{Duration: stale} } + case "storers": + storers, _ := defaultCacheV.Slice() + dc.Storers = make([]string, 0) + for _, storer := range storers { + h, _ := storer.String() + dc.Storers = append(dc.Storers, h) + } case "default_cache_control": dc.DefaultCacheControl, _ = defaultCacheV.String() } diff --git a/plugins/souin/agnostic/configuration_parser.go b/plugins/souin/agnostic/configuration_parser.go index 454699898..149967cec 100644 --- a/plugins/souin/agnostic/configuration_parser.go +++ b/plugins/souin/agnostic/configuration_parser.go @@ -256,6 +256,18 @@ func parseDefaultCache(dcConfiguration map[string]interface{}) *configurationtyp if err == nil { dc.Stale = configurationtypes.Duration{Duration: ttl} } + case "storers": + if storers, ok := defaultCacheV.([]string); ok { + dc.Storers = storers + } else { + if storers, ok := defaultCacheV.([]string); ok { + dc.Storers = storers + } else { + for _, sv := range defaultCacheV.([]interface{}) { + dc.Storers = append(dc.Storers, sv.(string)) + } + } + } case "default_cache_control": dc.DefaultCacheControl, _ = defaultCacheV.(string) } diff --git a/plugins/traefik/main.go b/plugins/traefik/main.go index 8381fbbb2..d4aba10cb 100644 --- a/plugins/traefik/main.go +++ b/plugins/traefik/main.go @@ -198,6 +198,8 @@ func parseConfiguration(c map[string]interface{}) Configuration { if err == nil { dc.Stale = configurationtypes.Duration{Duration: stale} } + case "storers": + dc.Storers = parseStringSlice(defaultCacheV) case "default_cache_control": dc.DefaultCacheControl = defaultCacheV.(string) } diff --git a/plugins/traefik/vendor/github.com/darkweak/souin/configurationtypes/types.go b/plugins/traefik/vendor/github.com/darkweak/souin/configurationtypes/types.go index 53aac6bfe..609f14103 100644 --- a/plugins/traefik/vendor/github.com/darkweak/souin/configurationtypes/types.go +++ b/plugins/traefik/vendor/github.com/darkweak/souin/configurationtypes/types.go @@ -229,6 +229,7 @@ type DefaultCache struct { Port Port `json:"port" yaml:"port"` Regex Regex `json:"regex" yaml:"regex"` Stale Duration `json:"stale" yaml:"stale"` + Storers []string `json:"storers" yaml:"storers"` Timeout Timeout `json:"timeout" yaml:"timeout"` TTL Duration `json:"ttl" yaml:"ttl"` DefaultCacheControl string `json:"default_cache_control" yaml:"default_cache_control"` @@ -314,6 +315,11 @@ func (d *DefaultCache) GetStale() time.Duration { return d.Stale.Duration } +// GetStale returns the stale duration +func (d *DefaultCache) GetStorers() []string { + return d.Storers +} + // GetDefaultCacheControl returns the default Cache-Control response header value when empty func (d *DefaultCache) GetDefaultCacheControl() string { return d.DefaultCacheControl @@ -335,6 +341,7 @@ type DefaultCacheInterface interface { GetKey() Key GetRegex() Regex GetStale() time.Duration + GetStorers() []string GetTimeout() Timeout GetTTL() time.Duration GetDefaultCacheControl() string diff --git a/plugins/webgo/souin_test.go b/plugins/webgo/souin_test.go index 1120324df..db9164166 100644 --- a/plugins/webgo/souin_test.go +++ b/plugins/webgo/souin_test.go @@ -15,7 +15,7 @@ import ( func Test_NewHTTPCache(t *testing.T) { s := NewHTTPCache(DevDefaultConfiguration) - if s.SouinBaseHandler.Storer == nil { + if s.SouinBaseHandler.Storers == nil || len(s.SouinBaseHandler.Storers) != 1 { t.Error("The storer must be set.") } c := middleware.BaseConfiguration{}