|
|
|
package httpcache
|
|
|
|
|
|
|
|
import (
|
|
|
|
"regexp"
|
|
|
|
"time"
|
|
|
|
|
|
|
|
"github.com/darkweak/souin/configurationtypes"
|
|
|
|
"github.com/darkweak/souin/plugins"
|
|
|
|
"github.com/go-kratos/kratos/v2/config"
|
|
|
|
)
|
|
|
|
|
|
|
|
const (
|
|
|
|
configurationKey = "httpcache"
|
|
|
|
path = "path"
|
|
|
|
url = "url"
|
|
|
|
configurationPK = "configuration"
|
|
|
|
)
|
|
|
|
|
|
|
|
func parseRecursively(values map[string]config.Value) map[string]interface{} {
|
|
|
|
result := make(map[string]interface{})
|
|
|
|
for key, value := range values {
|
|
|
|
if v, e := value.Bool(); e == nil {
|
|
|
|
result[key] = v
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
switch value.Load().(type) {
|
|
|
|
case int, int8, int16, int32, int64, uint, uint8, uint16, uint32, uint64:
|
|
|
|
if v, e := value.Int(); e == nil {
|
|
|
|
result[key] = v
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
case float32, float64:
|
|
|
|
if v, e := value.Float(); e == nil {
|
|
|
|
result[key] = v
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
}
|
|
|
|
svalue, _ := value.String()
|
|
|
|
if v, e := time.ParseDuration(svalue); e == nil {
|
|
|
|
result[key] = v
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
if v, e := value.Map(); e == nil {
|
|
|
|
result[key] = parseRecursively(v)
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return result
|
|
|
|
}
|
|
|
|
|
|
|
|
func parseAPI(apiConfiguration map[string]config.Value) configurationtypes.API {
|
|
|
|
var a configurationtypes.API
|
|
|
|
var prometheusConfiguration, souinConfiguration map[string]config.Value
|
|
|
|
|
|
|
|
for apiK, apiV := range apiConfiguration {
|
|
|
|
switch apiK {
|
|
|
|
case "prometheus":
|
|
|
|
prometheusConfiguration, _ = apiV.Map()
|
|
|
|
case "souin":
|
|
|
|
souinConfiguration, _ = apiV.Map()
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if prometheusConfiguration != nil {
|
|
|
|
a.Prometheus = configurationtypes.APIEndpoint{}
|
|
|
|
a.Prometheus.Enable = true
|
|
|
|
if prometheusConfiguration["basepath"] != nil {
|
|
|
|
a.Prometheus.BasePath, _ = prometheusConfiguration["basepath"].String()
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if souinConfiguration != nil {
|
|
|
|
a.Souin = configurationtypes.APIEndpoint{}
|
|
|
|
a.Souin.Enable = true
|
|
|
|
if souinConfiguration["basepath"] != nil {
|
|
|
|
a.Souin.BasePath, _ = souinConfiguration["basepath"].String()
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return a
|
|
|
|
}
|
|
|
|
|
|
|
|
func parseCacheKeys(ccConfiguration map[string]config.Value) map[configurationtypes.RegValue]configurationtypes.Key {
|
|
|
|
cacheKeys := make(map[configurationtypes.RegValue]configurationtypes.Key)
|
|
|
|
for cacheKeysConfigurationK, cacheKeysConfigurationV := range ccConfiguration {
|
|
|
|
ck := configurationtypes.Key{}
|
|
|
|
cacheKeysConfigurationVMap, _ := cacheKeysConfigurationV.Map()
|
|
|
|
for cacheKeysConfigurationVMapK := range cacheKeysConfigurationVMap {
|
|
|
|
switch cacheKeysConfigurationVMapK {
|
|
|
|
case "disable_body":
|
|
|
|
ck.DisableBody = true
|
|
|
|
case "disable_host":
|
|
|
|
ck.DisableHost = true
|
|
|
|
case "disable_method":
|
|
|
|
ck.DisableMethod = true
|
|
|
|
}
|
|
|
|
}
|
|
|
|
rg := regexp.MustCompile(cacheKeysConfigurationK)
|
|
|
|
cacheKeys[configurationtypes.RegValue{Regexp: rg}] = ck
|
|
|
|
}
|
|
|
|
|
|
|
|
return cacheKeys
|
|
|
|
}
|
|
|
|
|
|
|
|
func parseDefaultCache(dcConfiguration map[string]config.Value) *configurationtypes.DefaultCache {
|
|
|
|
dc := configurationtypes.DefaultCache{
|
|
|
|
Distributed: false,
|
|
|
|
Headers: []string{},
|
|
|
|
Olric: configurationtypes.CacheProvider{
|
|
|
|
URL: "",
|
|
|
|
Path: "",
|
|
|
|
Configuration: nil,
|
|
|
|
},
|
|
|
|
Regex: configurationtypes.Regex{},
|
|
|
|
TTL: configurationtypes.Duration{},
|
|
|
|
DefaultCacheControl: "",
|
|
|
|
}
|
|
|
|
for defaultCacheK, defaultCacheV := range dcConfiguration {
|
|
|
|
switch defaultCacheK {
|
|
|
|
case "allowed_http_verbs":
|
|
|
|
headers, _ := defaultCacheV.Slice()
|
|
|
|
dc.AllowedHTTPVerbs = make([]string, 0)
|
|
|
|
for _, header := range headers {
|
|
|
|
h, _ := header.String()
|
|
|
|
dc.AllowedHTTPVerbs = append(dc.AllowedHTTPVerbs, h)
|
|
|
|
}
|
|
|
|
case "badger":
|
|
|
|
provider := configurationtypes.CacheProvider{}
|
|
|
|
badgerConfiguration, _ := defaultCacheV.Map()
|
|
|
|
for badgerConfigurationK, badgerConfigurationV := range badgerConfiguration {
|
|
|
|
switch badgerConfigurationK {
|
|
|
|
case url:
|
|
|
|
provider.URL, _ = badgerConfigurationV.String()
|
|
|
|
case path:
|
|
|
|
provider.Path, _ = badgerConfigurationV.String()
|
|
|
|
case configurationPK:
|
|
|
|
configMap, e := badgerConfigurationV.Map()
|
|
|
|
if e == nil {
|
|
|
|
provider.Configuration = parseRecursively(configMap)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
dc.Badger = provider
|
|
|
|
case "cdn":
|
|
|
|
cdn := configurationtypes.CDN{}
|
|
|
|
cdnConfiguration, _ := defaultCacheV.Map()
|
|
|
|
for cdnConfigurationK, cdnConfigurationV := range cdnConfiguration {
|
|
|
|
switch cdnConfigurationK {
|
|
|
|
case "api_key":
|
|
|
|
cdn.APIKey, _ = cdnConfigurationV.String()
|
|
|
|
case "dynamic":
|
|
|
|
cdn.Dynamic = true
|
|
|
|
case "hostname":
|
|
|
|
cdn.Hostname, _ = cdnConfigurationV.String()
|
|
|
|
case "network":
|
|
|
|
cdn.Network, _ = cdnConfigurationV.String()
|
|
|
|
case "provider":
|
|
|
|
cdn.Provider, _ = cdnConfigurationV.String()
|
|
|
|
case "strategy":
|
|
|
|
cdn.Strategy, _ = cdnConfigurationV.String()
|
|
|
|
}
|
|
|
|
}
|
|
|
|
dc.CDN = cdn
|
|
|
|
case "etcd":
|
|
|
|
provider := configurationtypes.CacheProvider{}
|
|
|
|
etcdConfiguration, _ := defaultCacheV.Map()
|
|
|
|
for etcdConfigurationK, etcdConfigurationV := range etcdConfiguration {
|
|
|
|
switch etcdConfigurationK {
|
|
|
|
case url:
|
|
|
|
provider.URL, _ = etcdConfigurationV.String()
|
|
|
|
case path:
|
|
|
|
provider.Path, _ = etcdConfigurationV.String()
|
|
|
|
case configurationPK:
|
|
|
|
configMap, e := etcdConfigurationV.Map()
|
|
|
|
if e == nil {
|
|
|
|
provider.Configuration = parseRecursively(configMap)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
dc.Etcd = provider
|
|
|
|
case "headers":
|
|
|
|
headers, _ := defaultCacheV.Slice()
|
|
|
|
dc.Headers = make([]string, 0)
|
|
|
|
for _, header := range headers {
|
|
|
|
h, _ := header.String()
|
|
|
|
dc.Headers = append(dc.Headers, h)
|
|
|
|
}
|
|
|
|
case "nuts":
|
|
|
|
provider := configurationtypes.CacheProvider{}
|
|
|
|
nutsConfiguration, _ := defaultCacheV.Map()
|
|
|
|
for nutsConfigurationK, nutsConfigurationV := range nutsConfiguration {
|
|
|
|
switch nutsConfigurationK {
|
|
|
|
case url:
|
|
|
|
provider.URL, _ = nutsConfigurationV.String()
|
|
|
|
case path:
|
|
|
|
provider.Path, _ = nutsConfigurationV.String()
|
|
|
|
case configurationPK:
|
|
|
|
configMap, e := nutsConfigurationV.Map()
|
|
|
|
if e == nil {
|
|
|
|
provider.Configuration = parseRecursively(configMap)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
dc.Nuts = provider
|
|
|
|
case "olric":
|
|
|
|
provider := configurationtypes.CacheProvider{}
|
|
|
|
olricConfiguration, _ := defaultCacheV.Map()
|
|
|
|
for olricConfigurationK, olricConfigurationV := range olricConfiguration {
|
|
|
|
switch olricConfigurationK {
|
|
|
|
case url:
|
|
|
|
provider.URL, _ = olricConfigurationV.String()
|
|
|
|
case path:
|
|
|
|
provider.Path, _ = olricConfigurationV.String()
|
|
|
|
case configurationPK:
|
|
|
|
configMap, e := olricConfigurationV.Map()
|
|
|
|
if e == nil {
|
|
|
|
provider.Configuration = parseRecursively(configMap)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
dc.Distributed = true
|
|
|
|
dc.Olric = provider
|
|
|
|
case "regex":
|
|
|
|
regex, _ := defaultCacheV.Map()
|
|
|
|
exclude, _ := regex["exclude"].String()
|
|
|
|
if exclude != "" {
|
|
|
|
dc.Regex = configurationtypes.Regex{Exclude: exclude}
|
|
|
|
}
|
|
|
|
case "ttl":
|
|
|
|
sttl, err := defaultCacheV.String()
|
|
|
|
ttl, _ := time.ParseDuration(sttl)
|
|
|
|
if err == nil {
|
|
|
|
dc.TTL = configurationtypes.Duration{Duration: ttl}
|
|
|
|
}
|
|
|
|
case "stale":
|
|
|
|
sstale, err := defaultCacheV.String()
|
|
|
|
stale, _ := time.ParseDuration(sstale)
|
|
|
|
if err == nil {
|
|
|
|
dc.Stale = configurationtypes.Duration{Duration: stale}
|
|
|
|
}
|
|
|
|
case "default_cache_control":
|
|
|
|
dc.DefaultCacheControl, _ = defaultCacheV.String()
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return &dc
|
|
|
|
}
|
|
|
|
|
|
|
|
func parseURLs(urls map[string]config.Value) map[string]configurationtypes.URL {
|
|
|
|
u := make(map[string]configurationtypes.URL)
|
|
|
|
|
|
|
|
for urlK, urlV := range urls {
|
|
|
|
currentURL := configurationtypes.URL{
|
|
|
|
TTL: configurationtypes.Duration{},
|
|
|
|
Headers: nil,
|
|
|
|
}
|
|
|
|
currentValue, _ := urlV.Map()
|
|
|
|
if currentValue["headers"] != nil {
|
|
|
|
currentURL.Headers = make([]string, 0)
|
|
|
|
headers, _ := currentValue["headers"].Slice()
|
|
|
|
for _, header := range headers {
|
|
|
|
h, _ := header.String()
|
|
|
|
currentURL.Headers = append(currentURL.Headers, h)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
sttl, err := currentValue["ttl"].String()
|
|
|
|
ttl, _ := time.ParseDuration(sttl)
|
|
|
|
if err == nil {
|
|
|
|
currentURL.TTL = configurationtypes.Duration{Duration: ttl}
|
|
|
|
}
|
|
|
|
if _, exists := currentValue["default_cache_control"]; exists {
|
|
|
|
currentURL.DefaultCacheControl, _ = currentValue["default_cache_control"].String()
|
|
|
|
}
|
|
|
|
u[urlK] = currentURL
|
|
|
|
}
|
|
|
|
|
|
|
|
return u
|
|
|
|
}
|
|
|
|
|
|
|
|
func parseSurrogateKeys(surrogates map[string]config.Value) map[string]configurationtypes.SurrogateKeys {
|
|
|
|
u := make(map[string]configurationtypes.SurrogateKeys)
|
|
|
|
|
|
|
|
for surrogateK, surrogateV := range surrogates {
|
|
|
|
surrogate := configurationtypes.SurrogateKeys{}
|
|
|
|
currentValue, _ := surrogateV.Map()
|
|
|
|
for key, value := range currentValue {
|
|
|
|
switch key {
|
|
|
|
case "headers":
|
|
|
|
surrogate.Headers = map[string]string{}
|
|
|
|
headers, e := value.Map()
|
|
|
|
if e == nil {
|
|
|
|
for hKey, hValue := range headers {
|
|
|
|
v, _ := hValue.String()
|
|
|
|
surrogate.Headers[hKey] = v
|
|
|
|
}
|
|
|
|
}
|
|
|
|
case "url":
|
|
|
|
surl, _ := currentValue["url"].String()
|
|
|
|
surrogate.URL = surl
|
|
|
|
}
|
|
|
|
}
|
|
|
|
u[surrogateK] = surrogate
|
|
|
|
}
|
|
|
|
|
|
|
|
return u
|
|
|
|
}
|
|
|
|
|
|
|
|
// ParseConfiguration parse the Kratos configuration into a valid HTTP
|
|
|
|
// cache configuration object.
|
|
|
|
func ParseConfiguration(c config.Config) plugins.BaseConfiguration {
|
|
|
|
var configuration plugins.BaseConfiguration
|
|
|
|
|
|
|
|
values, _ := c.Value(configurationKey).Map()
|
|
|
|
for key, v := range values {
|
|
|
|
switch key {
|
|
|
|
case "api":
|
|
|
|
apiConfiguration, _ := v.Map()
|
|
|
|
configuration.API = parseAPI(apiConfiguration)
|
|
|
|
case "cache_keys":
|
|
|
|
cacheKeysConfiguration, _ := v.Map()
|
|
|
|
configuration.CacheKeys = parseCacheKeys(cacheKeysConfiguration)
|
|
|
|
case "default_cache":
|
|
|
|
defaultCache, _ := v.Map()
|
|
|
|
configuration.DefaultCache = parseDefaultCache(defaultCache)
|
|
|
|
case "log_level":
|
|
|
|
configuration.LogLevel, _ = v.String()
|
|
|
|
case "urls":
|
|
|
|
urls, _ := v.Map()
|
|
|
|
configuration.URLs = parseURLs(urls)
|
|
|
|
case "ykeys":
|
|
|
|
ykeys, _ := v.Map()
|
|
|
|
configuration.Ykeys = parseSurrogateKeys(ykeys)
|
|
|
|
case "surrogate_keys":
|
|
|
|
surrogates, _ := v.Map()
|
|
|
|
configuration.SurrogateKeys = parseSurrogateKeys(surrogates)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return configuration
|
|
|
|
}
|