Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 8 additions & 8 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -17,18 +17,18 @@ jobs:
# The Windows build currently fail because of https://github.com/golang/go/issues/40795, and because xcaddy isn't compatible with the known workaround
#os: [ ubuntu-latest, macos-latest, windows-latest ]
os: [ ubuntu-latest, macos-latest ]
go: [ '1.21' ]
go: [ '1.24' ]

runs-on: ${{ matrix.os }}

steps:
- name: Install Go
uses: actions/setup-go@v4
uses: actions/setup-go@v5
with:
go-version: ${{ matrix.go }}

- name: Checkout code
uses: actions/checkout@v3
uses: actions/checkout@v4

- name: Print Go version and environment
id: vars
Expand All @@ -44,7 +44,7 @@ jobs:
echo "::set-output name=go_cache::$(go env GOCACHE)"

- name: Cache the build cache
uses: actions/cache@v2
uses: actions/cache@v4
with:
path: ${{ steps.vars.outputs.go_cache }}
key: ${{ runner.os }}-${{ matrix.go }}-go-ci-${{ hashFiles('**/go.sum') }}
Expand All @@ -71,11 +71,11 @@ jobs:
runs-on: ubuntu-latest

steps:
- uses: actions/checkout@v3
- uses: actions/setup-go@v4
- uses: actions/checkout@v4
- uses: actions/setup-go@v5
with:
go-version: '1.21'
go-version: '1.24'
- name: golangci-lint
uses: golangci/golangci-lint-action@v3
uses: golangci/golangci-lint-action@v8
with:
args: --timeout 5m
85 changes: 85 additions & 0 deletions admin.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
package httpcache

import (
"fmt"
"net/http"
"strings"
"time"

"github.com/caddyserver/caddy/v2"
"github.com/darkweak/souin/configurationtypes"
"github.com/darkweak/souin/pkg/api"

"github.com/darkweak/storages/core"
)

func init() {
caddy.RegisterModule(new(adminAPI))
}

// adminAPI is a module that serves PKI endpoints to retrieve
// information about the CAs being managed by Caddy.
type adminAPI struct {
ctx caddy.Context
logger core.Logger
app *SouinApp
InternalEndpointHandlers *api.MapHandler
}

// CaddyModule returns the Caddy module information.
func (adminAPI) CaddyModule() caddy.ModuleInfo {
return caddy.ModuleInfo{
ID: "admin.api.souin",
New: func() caddy.Module { return new(adminAPI) },
}
}

func (a *adminAPI) handleAPIEndpoints(writer http.ResponseWriter, request *http.Request) error {
if a.InternalEndpointHandlers != nil {
for k, handler := range *a.InternalEndpointHandlers.Handlers {
if strings.Contains(request.RequestURI, k) {
handler(writer, request)
return nil
}
}
}

return caddy.APIError{
HTTPStatus: http.StatusNotFound,
Err: fmt.Errorf("resource not found: %v", request.URL.Path),
}
}

// Provision sets up the adminAPI module.
func (a *adminAPI) Provision(ctx caddy.Context) error {
a.ctx = ctx
a.logger = ctx.Logger(a).Sugar()

app, err := ctx.App(moduleName)
if err != nil {
return err
}

a.app = app.(*SouinApp)
config := Configuration{
API: a.app.API,
DefaultCache: DefaultCache{
TTL: configurationtypes.Duration{
Duration: 120 * time.Second,
},
},
}
a.InternalEndpointHandlers = api.GenerateHandlerMap(&config, a.app.Storers, a.app.SurrogateStorage)

return nil
}

// Routes returns the admin routes.
func (a *adminAPI) Routes() []caddy.AdminRoute {
return []caddy.AdminRoute{
{
Pattern: "/{params...}",
Handler: caddy.AdminHandlerFunc(a.handleAPIEndpoints),
},
}
}
20 changes: 12 additions & 8 deletions app.go
Original file line number Diff line number Diff line change
@@ -1,8 +1,6 @@
package httpcache

import (
"errors"

"github.com/caddyserver/caddy/v2"
"github.com/darkweak/souin/configurationtypes"
"github.com/darkweak/souin/pkg/storage/types"
Expand All @@ -15,8 +13,10 @@ type SouinApp struct {
DefaultCache
// The provider to use.
Storers []types.Storer
// Surrogate storage to support th econfiguration reload without surrogate-key data loss.
// Surrogate storage to support the configuration reload without surrogate-key data loss.
SurrogateStorage providers.SurrogateInterface
// SurrogateKeyDisabled opt-out the Surrogate key system.
SurrogateKeyDisabled bool
// Cache-key tweaking.
CacheKeys configurationtypes.CacheKeys `json:"cache_keys,omitempty"`
// API endpoints enablers.
Expand All @@ -29,14 +29,17 @@ func init() {
caddy.RegisterModule(SouinApp{})
}

// Provision implements caddy.Provisioner
func (s SouinApp) Provision(_ caddy.Context) error {
return nil
}

// Start will start the App
func (s SouinApp) Start() error {
core.ResetRegisteredStorages()
_, _ = up.Delete(stored_providers_key)
_, _ = up.LoadOrStore(stored_providers_key, newStorageProvider())
if s.DefaultCache.GetTTL() == 0 {
return errors.New("Invalid/Incomplete default cache declaration")
}

return nil
}

Expand All @@ -54,6 +57,7 @@ func (s SouinApp) CaddyModule() caddy.ModuleInfo {
}

var (
_ caddy.App = (*SouinApp)(nil)
_ caddy.Module = (*SouinApp)(nil)
_ caddy.App = (*SouinApp)(nil)
_ caddy.Module = (*SouinApp)(nil)
_ caddy.Provisioner = (*SouinApp)(nil)
)
4 changes: 2 additions & 2 deletions cleaner.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,8 +21,8 @@ func newStorageProvider() *storage_providers {
}

func (s *storage_providers) Add(key interface{}) {
s.RWMutex.Lock()
defer s.RWMutex.Unlock()
s.Lock()
defer s.Unlock()

s.list[key] = true
}
Expand Down
64 changes: 56 additions & 8 deletions configuration.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,8 @@ import (
type DefaultCache struct {
// Allowed HTTP verbs to be cached by the system.
AllowedHTTPVerbs []string `json:"allowed_http_verbs"`
// Allowed additional status code to be cached by the system.
AllowedAdditionalStatusCodes []int `json:"allowed_additional_status_codes"`
// Badger provider configuration.
Badger configurationtypes.CacheProvider `json:"badger"`
// The cache name to use in the Cache-Status response header.
Expand Down Expand Up @@ -65,6 +67,11 @@ func (d *DefaultCache) GetAllowedHTTPVerbs() []string {
return d.AllowedHTTPVerbs
}

// GetAllowedAdditionalStatusCodes returns the allowed verbs to cache
func (d *DefaultCache) GetAllowedAdditionalStatusCodes() []int {
return d.AllowedAdditionalStatusCodes
}

// GetBadger returns the Badger configuration
func (d *DefaultCache) GetBadger() configurationtypes.CacheProvider {
return d.Badger
Expand Down Expand Up @@ -189,15 +196,17 @@ type Configuration struct {
LogLevel string
// SurrogateKeys contains the surrogate keys to use with a predefined mapping
SurrogateKeys map[string]configurationtypes.SurrogateKeys
logger core.Logger
// SurrogateKeyDisabled disables the surrogate keys system
SurrogateKeyDisabled bool
logger core.Logger
}

// GetUrls get the urls list in the configuration
func (c *Configuration) GetUrls() map[string]configurationtypes.URL {
return c.URLs
}

// GetDefaultCache get the default cache
// GetPluginName get the plugin name
func (c *Configuration) GetPluginName() string {
return "caddy"
}
Expand Down Expand Up @@ -237,6 +246,11 @@ func (c *Configuration) GetSurrogateKeys() map[string]configurationtypes.Surroga
return nil
}

// IsSurrogateDisabled disables the surrogate storage
func (c *Configuration) IsSurrogateDisabled() bool {
return c.SurrogateKeyDisabled
}

// GetCacheKeys get the cache keys rules to override
func (c *Configuration) GetCacheKeys() configurationtypes.CacheKeys {
return c.CacheKeys
Expand Down Expand Up @@ -271,6 +285,12 @@ func parseBadgerConfiguration(c map[string]interface{}) map[string]interface{} {
c[k] = v
case "SyncWrites", "ReadOnly", "InMemory", "MetricsEnabled", "CompactL0OnClose", "LmaxCompaction", "VerifyValueChecksum", "BypassLockGuard", "DetectConflicts":
c[k] = true
if v != nil {
val, ok := v.(string)
if ok {
c[k], _ = strconv.ParseBool(val)
}
}
case "NumVersionsToKeep", "NumGoroutines", "MemTableSize", "BaseTableSize", "BaseLevelSize", "LevelSizeMultiplier", "TableSizeMultiplier", "MaxLevels", "ValueThreshold", "NumMemtables", "BlockSize", "BlockCacheSize", "IndexCacheSize", "NumLevelZeroTables", "NumLevelZeroTablesStall", "ValueLogFileSize", "NumCompactors", "ZSTDCompressionLevel", "ChecksumVerificationMode", "NamespaceOffset":
c[k], _ = strconv.Atoi(v.(string))
case "Compression", "ValueLogMaxEntries":
Expand Down Expand Up @@ -301,11 +321,12 @@ func parseRedisConfiguration(c map[string]interface{}) map[string]interface{} {
case "SendToReplicas", "ShuffleInit", "ClientNoTouch", "DisableRetry", "DisableCache", "AlwaysPipelining", "AlwaysRESP2", "ForceSingleClient", "ReplicaOnly", "ClientNoEvict", "ContextTimeoutEnabled", "PoolFIFO", "ReadOnly", "RouteByLatency", "RouteRandomly", "DisableIndentity":
c[k] = true
case "SelectDB", "CacheSizeEachConn", "RingScaleEachConn", "ReadBufferEachConn", "WriteBufferEachConn", "BlockingPoolSize", "PipelineMultiplex", "DB", "Protocol", "MaxRetries", "PoolSize", "MinIdleConns", "MaxIdleConns", "MaxActiveConns", "MaxRedirects":
if v == false {
switch v {
case false:
c[k] = 0
} else if v == true {
case true:
c[k] = 1
} else {
default:
c[k], _ = strconv.Atoi(v.(string))
}
case "ConnWriteTimeout", "MaxFlushDelay", "MinRetryBackoff", "MaxRetryBackoff", "DialTimeout", "ReadTimeout", "WriteTimeout", "PoolTimeout", "ConnMaxIdleTime", "ConnMaxLifetime":
Expand Down Expand Up @@ -342,11 +363,21 @@ func parseSimpleFSConfiguration(c map[string]interface{}) map[string]interface{}
case "path":
c[k] = v
case "size":
if v == false {
switch v {
case false:
c[k] = 0
} else if v == true {
case true:
c[k] = 1
} else {
default:
c[k], _ = strconv.Atoi(v.(string))
}
case "directory_size":
switch v {
case false:
c[k] = 0
case true:
c[k] = 1
default:
c[k], _ = strconv.Atoi(v.(string))
}
}
Expand All @@ -364,6 +395,17 @@ func parseConfiguration(cfg *Configuration, h *caddyfile.Dispenser, isGlobal boo
allowed := cfg.DefaultCache.AllowedHTTPVerbs
allowed = append(allowed, h.RemainingArgs()...)
cfg.DefaultCache.AllowedHTTPVerbs = allowed
case "allowed_additional_status_codes":
allowed := cfg.DefaultCache.AllowedAdditionalStatusCodes
additional := h.RemainingArgs()
codes := make([]int, 0)
for _, code := range additional {
if c, err := strconv.Atoi(code); err == nil {
codes = append(codes, c)
}
}
allowed = append(allowed, codes...)
cfg.DefaultCache.AllowedAdditionalStatusCodes = allowed
case "api":
if !isGlobal {
return h.Err("'api' block must be global")
Expand Down Expand Up @@ -453,6 +495,8 @@ func parseConfiguration(cfg *Configuration, h *caddyfile.Dispenser, isGlobal boo
ck.DisableQuery = true
case "disable_scheme":
ck.DisableScheme = true
case "disable_vary":
ck.DisableVary = true
case "template":
ck.Template = h.RemainingArgs()[0]
case "hash":
Expand Down Expand Up @@ -547,6 +591,8 @@ func parseConfiguration(cfg *Configuration, h *caddyfile.Dispenser, isGlobal boo
config_key.DisableQuery = true
case "disable_scheme":
config_key.DisableScheme = true
case "disable_vary":
config_key.DisableVary = true
case "template":
config_key.Template = h.RemainingArgs()[0]
case "hash":
Expand Down Expand Up @@ -720,6 +766,8 @@ func parseConfiguration(cfg *Configuration, h *caddyfile.Dispenser, isGlobal boo
}
case "disable_coalescing":
cfg.DefaultCache.DisableCoalescing = true
case "disable_surrogate_key":
cfg.SurrogateKeyDisabled = true
default:
return h.Errf("unsupported root directive: %s", rootOption)
}
Expand Down
Loading
Loading