Skip to content

Commit

Permalink
feat(middleware): Add configuration option MaxBodyBytes
Browse files Browse the repository at this point in the history
  • Loading branch information
Vincent Jordan committed Jan 26, 2024
1 parent 5a88d84 commit 195b0e0
Show file tree
Hide file tree
Showing 4 changed files with 32 additions and 1 deletion.
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -214,6 +214,7 @@ surrogate_keys:
| `default_cache.timeout.cache` | The timeout duration to consider the cache provider as unreachable | `10ms` |
| `default_cache.ttl` | The TTL duration | `120s` |
| `default_cache.default_cache_control` | Set the default value of `Cache-Control` response header if not set by upstream (Souin treats empty `Cache-Control` as `public` if omitted) | `no-store` |
| `default_cache.max_body_bytes` | Set the maximum size (in bytes) for a response body to be cached (unlimited if omited) | `1048576` (1MB) |
| `log_level` | The log level | `One of DEBUG, INFO, WARN, ERROR, DPANIC, PANIC, FATAL it's case insensitive` |
| `reverse_proxy_url` | The reverse-proxy's instance URL (Apache, Nginx, Træfik...) | - `http://yourservice` (Container way)<br/>`http://localhost:81` (Local way)<br/>`http://yourdomain.com:81` (Network way) |
| `ssl_providers` | List of your providers handling certificates | `- traefik`<br/><br/>`- nginx`<br/><br/>`- apache` |
Expand Down
7 changes: 7 additions & 0 deletions configurationtypes/types.go
Original file line number Diff line number Diff line change
Expand Up @@ -233,6 +233,7 @@ type DefaultCache struct {
Timeout Timeout `json:"timeout" yaml:"timeout"`
TTL Duration `json:"ttl" yaml:"ttl"`
DefaultCacheControl string `json:"default_cache_control" yaml:"default_cache_control"`
MaxBodyBytes uint64 `json:"max_body_bytes" yaml:"max_body_bytes"`
}

// GetAllowedHTTPVerbs returns the allowed verbs to cache
Expand Down Expand Up @@ -325,6 +326,11 @@ func (d *DefaultCache) GetDefaultCacheControl() string {
return d.DefaultCacheControl
}

// GetMaxBodyBytes returns the default maximum body size (in bytes) for storing into cache
func (d *DefaultCache) GetMaxBodyBytes() uint64 {
return d.MaxBodyBytes
}

// DefaultCacheInterface interface
type DefaultCacheInterface interface {
GetAllowedHTTPVerbs() []string
Expand All @@ -345,6 +351,7 @@ type DefaultCacheInterface interface {
GetTimeout() Timeout
GetTTL() time.Duration
GetDefaultCacheControl() string
GetMaxBodyBytes() uint64
}

// APIEndpoint is the minimal structure to define an endpoint
Expand Down
10 changes: 9 additions & 1 deletion pkg/middleware/middleware.go
Original file line number Diff line number Diff line change
Expand Up @@ -227,6 +227,12 @@ func (s *SouinBaseHandler) Store(
bLen := customWriter.Buf.Len()
customWriter.mutex.Unlock()

respBodyMaxSize := s.Configuration.GetDefaultCache().GetMaxBodyBytes()
bodyTooLarge := false
if (respBodyMaxSize > 0) && (bLen > int(respBodyMaxSize)) {
bodyTooLarge = true
}

res := http.Response{
StatusCode: statusCode,
Body: io.NopCloser(bytes.NewBuffer(b)),
Expand All @@ -241,7 +247,7 @@ func (s *SouinBaseHandler) Store(
}
res.Header.Set(rfc.StoredLengthHeader, res.Header.Get("Content-Length"))
response, err := httputil.DumpResponse(&res, true)
if err == nil && bLen > 0 {
if err == nil && bLen > 0 && !bodyTooLarge {
variedHeaders, isVaryStar := rfc.VariedHeaderAllCommaSepValues(res.Header)
if isVaryStar {
// "Implies that the response is uncacheable"
Expand Down Expand Up @@ -285,6 +291,8 @@ func (s *SouinBaseHandler) Store(
}
}

} else if err == nil && bodyTooLarge {
status += "; detail=UPSTREAM-RESPONSE-TOO-LARGE"
} else {
status += "; detail=UPSTREAM-ERROR-OR-EMPTY-RESPONSE"
}
Expand Down
15 changes: 15 additions & 0 deletions plugins/caddy/configuration.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,8 @@ type DefaultCache struct {
CDN configurationtypes.CDN `json:"cdn"`
// The default Cache-Control header value if none set by the upstream server.
DefaultCacheControl string `json:"default_cache_control"`
// The maximum body size (in bytes) to be stored into cache.
MaxBodyBytes uint64 `json:"max_body_bytes"`
// Redis provider configuration.
Distributed bool `json:"distributed"`
// Headers to add to the cache key if they are present.
Expand Down Expand Up @@ -140,6 +142,11 @@ func (d *DefaultCache) GetDefaultCacheControl() string {
return d.DefaultCacheControl
}

// GetMaxBodyBytes returns the maximum body size (in bytes) to be cached
func (d *DefaultCache) GetMaxBodyBytes() uint64 {
return d.MaxBodyBytes
}

// Configuration holder
type Configuration struct {
// Default cache to fallback on when none are redefined.
Expand Down Expand Up @@ -407,6 +414,14 @@ func parseConfiguration(cfg *Configuration, h *caddyfile.Dispenser, isBlocking b
case "default_cache_control":
args := h.RemainingArgs()
cfg.DefaultCache.DefaultCacheControl = strings.Join(args, " ")
case "max_body_bytes":
args := h.RemainingArgs()
maxBodyBytes, err := strconv.ParseUint(args[0], 10, 64)
if err != nil {
return h.Errf("unsupported max_body_bytes: %s", args)
} else {
cfg.DefaultCache.MaxBodyBytes = maxBodyBytes
}
case "etcd":
cfg.DefaultCache.Distributed = true
provider := configurationtypes.CacheProvider{}
Expand Down

0 comments on commit 195b0e0

Please sign in to comment.