Skip to content

Commit e03395b

Browse files
authored
feat(configuration): extends with allowed_addional_status_code (#577)
* feat(configuration): extends with allowed_addional_status_code * fix(status-code-extension): allow empty responses to be cached * fix(typo): allowed_additional_status_codes
1 parent f75ca71 commit e03395b

File tree

53 files changed

+406
-140
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

53 files changed

+406
-140
lines changed

README.md

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -106,6 +106,9 @@ default_cache:
106106
- GET
107107
- POST
108108
- HEAD
109+
allowed_additional_status_codes: # Allowed additional HTTP status code to cache.
110+
- 202
111+
- 400
109112
cache_name: Souin # Override the cache name to use in the Cache-Status header
110113
distributed: true # Use Olric or Etcd distributed storage
111114
key:
@@ -196,6 +199,7 @@ surrogate_keys:
196199
| `cdn.service_id` | The service id if required, depending the provider | `123456_id` |
197200
| `cdn.zone_id` | The zone id if required, depending the provider | `anywhere_zone` |
198201
| `default_cache.allowed_http_verbs` | The HTTP verbs to support cache | `- GET`<br/><br/>`- POST`<br/><br/>`(default: GET, HEAD)` |
202+
| `default_cache.allowed_additional_status_codes` | The additional HTTP status code to support cache | `- 200`<br/><br/>`- 404` |
199203
| `default_cache.badger` | Configure the Badger cache storage | |
200204
| `default_cache.badger.path` | Configure Badger with a file | `/anywhere/badger_configuration.json` |
201205
| `default_cache.badger.configuration` | Configure Badger directly in the Caddyfile or your JSON caddy configuration | [See the Badger configuration for the options](https://dgraph.io/docs/badger/get-started/) |
@@ -425,6 +429,7 @@ There is the fully configuration below
425429
}
426430
cache {
427431
allowed_http_verbs GET POST PATCH
432+
allowed_additional_status_codes 202
428433
api {
429434
basepath /some-basepath
430435
prometheus {
@@ -882,6 +887,9 @@ http:
882887
- GET
883888
- POST
884889
- HEAD
890+
allowed_additional_status_codes:
891+
- 202
892+
- 400
885893
cdn:
886894
api_key: XXXX
887895
dynamic: true
@@ -979,6 +987,9 @@ http:
979987
- GET
980988
- HEAD
981989
- POST
990+
allowed_additional_status_codes:
991+
- 202
992+
- 400
982993
default_cache_control: no-store
983994
log_level: debug
984995
urls:

configuration/configuration.sample.yml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,9 @@ default_cache: # Required part
1818
- GET
1919
- POST
2020
- HEAD
21+
allowed_additional_status_codes: # Allowed additional HTTP status code to cache.
22+
- 202
23+
- 400
2124
cache_name: Souin # Override the Cache-Status name
2225
distributed: true # Use Olric distributed storage
2326
headers: # Default headers concatenated in stored keys

configurationtypes/types.go

Lines changed: 31 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -230,37 +230,43 @@ type Key struct {
230230

231231
// DefaultCache configuration
232232
type DefaultCache struct {
233-
AllowedHTTPVerbs []string `json:"allowed_http_verbs" yaml:"allowed_http_verbs"`
234-
Badger CacheProvider `json:"badger" yaml:"badger"`
235-
CDN CDN `json:"cdn" yaml:"cdn"`
236-
CacheName string `json:"cache_name" yaml:"cache_name"`
237-
Distributed bool `json:"distributed" yaml:"distributed"`
238-
Headers []string `json:"headers" yaml:"headers"`
239-
Key Key `json:"key" yaml:"key"`
240-
Etcd CacheProvider `json:"etcd" yaml:"etcd"`
241-
Mode string `json:"mode" yaml:"mode"`
242-
Nats CacheProvider `json:"nats" yaml:"nats"`
243-
Nuts CacheProvider `json:"nuts" yaml:"nuts"`
244-
Olric CacheProvider `json:"olric" yaml:"olric"`
245-
Otter CacheProvider `json:"otter" yaml:"otter"`
246-
Redis CacheProvider `json:"redis" yaml:"redis"`
247-
Port Port `json:"port" yaml:"port"`
248-
Regex Regex `json:"regex" yaml:"regex"`
249-
SimpleFS CacheProvider `json:"simplefs" yaml:"simplefs"`
250-
Stale Duration `json:"stale" yaml:"stale"`
251-
Storers []string `json:"storers" yaml:"storers"`
252-
Timeout Timeout `json:"timeout" yaml:"timeout"`
253-
TTL Duration `json:"ttl" yaml:"ttl"`
254-
DefaultCacheControl string `json:"default_cache_control" yaml:"default_cache_control"`
255-
MaxBodyBytes uint64 `json:"max_cacheable_body_bytes" yaml:"max_cacheable_body_bytes"`
256-
DisableCoalescing bool `json:"disable_coalescing" yaml:"disable_coalescing"`
233+
AllowedHTTPVerbs []string `json:"allowed_http_verbs" yaml:"allowed_http_verbs"`
234+
AllowedAdditionalStatusCodes []int `json:"allowed_additional_status_codes" yaml:"allowed_additional_status_codes"`
235+
Badger CacheProvider `json:"badger" yaml:"badger"`
236+
CDN CDN `json:"cdn" yaml:"cdn"`
237+
CacheName string `json:"cache_name" yaml:"cache_name"`
238+
Distributed bool `json:"distributed" yaml:"distributed"`
239+
Headers []string `json:"headers" yaml:"headers"`
240+
Key Key `json:"key" yaml:"key"`
241+
Etcd CacheProvider `json:"etcd" yaml:"etcd"`
242+
Mode string `json:"mode" yaml:"mode"`
243+
Nats CacheProvider `json:"nats" yaml:"nats"`
244+
Nuts CacheProvider `json:"nuts" yaml:"nuts"`
245+
Olric CacheProvider `json:"olric" yaml:"olric"`
246+
Otter CacheProvider `json:"otter" yaml:"otter"`
247+
Redis CacheProvider `json:"redis" yaml:"redis"`
248+
Port Port `json:"port" yaml:"port"`
249+
Regex Regex `json:"regex" yaml:"regex"`
250+
SimpleFS CacheProvider `json:"simplefs" yaml:"simplefs"`
251+
Stale Duration `json:"stale" yaml:"stale"`
252+
Storers []string `json:"storers" yaml:"storers"`
253+
Timeout Timeout `json:"timeout" yaml:"timeout"`
254+
TTL Duration `json:"ttl" yaml:"ttl"`
255+
DefaultCacheControl string `json:"default_cache_control" yaml:"default_cache_control"`
256+
MaxBodyBytes uint64 `json:"max_cacheable_body_bytes" yaml:"max_cacheable_body_bytes"`
257+
DisableCoalescing bool `json:"disable_coalescing" yaml:"disable_coalescing"`
257258
}
258259

259260
// GetAllowedHTTPVerbs returns the allowed verbs to cache
260261
func (d *DefaultCache) GetAllowedHTTPVerbs() []string {
261262
return d.AllowedHTTPVerbs
262263
}
263264

265+
// GetAllowedAdditionalStatusCodes returns the allowed verbs to cache
266+
func (d *DefaultCache) GetAllowedAdditionalStatusCodes() []int {
267+
return d.AllowedAdditionalStatusCodes
268+
}
269+
264270
// GetBadger returns the Badger configuration
265271
func (d *DefaultCache) GetBadger() CacheProvider {
266272
return d.Badger
@@ -374,6 +380,7 @@ func (d *DefaultCache) IsCoalescingDisable() bool {
374380
// DefaultCacheInterface interface
375381
type DefaultCacheInterface interface {
376382
GetAllowedHTTPVerbs() []string
383+
GetAllowedAdditionalStatusCodes() []int
377384
GetBadger() CacheProvider
378385
GetCacheName() string
379386
GetCDN() CDN

docs/website/content/docs/configuration.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -122,6 +122,10 @@ The default_cache prefix configure the default cache behavior. (e.g. `default_ca
122122
The allowed_http_verbs prefix configure the HTTP verbs allowed to get cached. (e.g. `default_cache.allowed_http_verbs`).
123123
default: `[GET, HEAD]`
124124

125+
#### Allowed additional status code
126+
The allowed_additional_status_codes prefix configure the additional HTTP status codes allowed to get cached. (e.g. `default_cache.allowed_additional_status_codes`).
127+
default: `[]`
128+
125129
#### Badger
126130
The badger prefix configure the badger storage. (e.g. `default_cache.badger`).
127131

pkg/middleware/middleware.go

Lines changed: 13 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -204,6 +204,16 @@ func canBypassAuthorizationRestriction(headers http.Header, bypassed []string) b
204204
return strings.Contains(strings.ToLower(headers.Get("Vary")), "authorization")
205205
}
206206

207+
func (s *SouinBaseHandler) hasAllowedAdditionalStatusCodesToCache(code int) bool {
208+
for _, sc := range s.Configuration.GetDefaultCache().GetAllowedAdditionalStatusCodes() {
209+
if sc == code {
210+
return true
211+
}
212+
}
213+
214+
return false
215+
}
216+
207217
func (s *SouinBaseHandler) Store(
208218
customWriter *CustomWriter,
209219
rq *http.Request,
@@ -212,7 +222,7 @@ func (s *SouinBaseHandler) Store(
212222
uri string,
213223
) error {
214224
statusCode := customWriter.GetStatusCode()
215-
if !isCacheableCode(statusCode) {
225+
if !isCacheableCode(statusCode) && !s.hasAllowedAdditionalStatusCodesToCache(statusCode) {
216226
customWriter.Header().Set("Cache-Status", fmt.Sprintf("%s; fwd=uri-miss; key=%s; detail=UNCACHEABLE-STATUS-CODE", rq.Context().Value(context.CacheName), rfc.GetCacheKeyFromCtx(rq.Context())))
217227

218228
switch statusCode {
@@ -323,7 +333,7 @@ func (s *SouinBaseHandler) Store(
323333
}
324334
res.Header.Set(rfc.StoredLengthHeader, res.Header.Get("Content-Length"))
325335
response, err := httputil.DumpResponse(&res, true)
326-
if err == nil && (bLen > 0 || canStatusCodeEmptyContent(statusCode)) {
336+
if err == nil && (bLen > 0 || canStatusCodeEmptyContent(statusCode) || s.hasAllowedAdditionalStatusCodesToCache(statusCode)) {
327337
variedHeaders, isVaryStar := rfc.VariedHeaderAllCommaSepValues(res.Header)
328338
if isVaryStar {
329339
// "Implies that the response is uncacheable"
@@ -442,7 +452,7 @@ func (s *SouinBaseHandler) Upstream(
442452
s.SurrogateKeyStorer.Invalidate(rq.Method, customWriter.Header())
443453

444454
statusCode := customWriter.GetStatusCode()
445-
if !isCacheableCode(statusCode) {
455+
if !isCacheableCode(statusCode) && !s.hasAllowedAdditionalStatusCodesToCache(statusCode) {
446456
customWriter.Header().Set("Cache-Status", fmt.Sprintf("%s; fwd=uri-miss; key=%s; detail=UNCACHEABLE-STATUS-CODE", rq.Context().Value(context.CacheName), rfc.GetCacheKeyFromCtx(rq.Context())))
447457

448458
switch statusCode {

plugins/beego/go.mod

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,7 @@ require (
4848
github.com/darkweak/storages/olric v0.0.8 // indirect
4949
github.com/darkweak/storages/otter v0.0.8 // indirect
5050
github.com/darkweak/storages/redis v0.0.8 // indirect
51+
github.com/darkweak/storages/simplefs v0.0.11 // indirect
5152
github.com/dgraph-io/badger v1.6.2 // indirect
5253
github.com/dgraph-io/badger/v2 v2.2007.4 // indirect
5354
github.com/dgraph-io/badger/v3 v3.2103.5 // indirect
@@ -94,6 +95,7 @@ require (
9495
github.com/jackc/pgservicefile v0.0.0-20221227161230-091c0ba34f0a // indirect
9596
github.com/jackc/pgtype v1.14.0 // indirect
9697
github.com/jackc/pgx/v4 v4.18.3 // indirect
98+
github.com/jellydator/ttlcache/v3 v3.3.0 // indirect
9799
github.com/klauspost/compress v1.17.8 // indirect
98100
github.com/klauspost/cpuid/v2 v2.2.7 // indirect
99101
github.com/libdns/libdns v0.2.2 // indirect
@@ -168,7 +170,7 @@ require (
168170
golang.org/x/exp v0.0.0-20240506185415-9bf2ced13842 // indirect
169171
golang.org/x/mod v0.17.0 // indirect
170172
golang.org/x/net v0.25.0 // indirect
171-
golang.org/x/sync v0.7.0 // indirect
173+
golang.org/x/sync v0.8.0 // indirect
172174
golang.org/x/sys v0.20.0 // indirect
173175
golang.org/x/term v0.20.0 // indirect
174176
golang.org/x/text v0.15.0 // indirect

plugins/beego/go.sum

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -157,6 +157,8 @@ github.com/darkweak/storages/otter v0.0.8 h1:Tsv4NiLAiZyvovs4oCWT+WQUYe5YvbFvEx2
157157
github.com/darkweak/storages/otter v0.0.8/go.mod h1:BBYxyWClX3PFdbvns6W95kLNNihq1FxljBYGIzP4snI=
158158
github.com/darkweak/storages/redis v0.0.8 h1:0CHLkImyaI/sYs+IOurYLAxFkrmz5dFblhfpF7oGhQc=
159159
github.com/darkweak/storages/redis v0.0.8/go.mod h1:pypJ5T3hweQWfHzFUjmZWeb1KaNK3ikNg1+rn0G+rD0=
160+
github.com/darkweak/storages/simplefs v0.0.11 h1:BagJxTJVtWCnCsQi4kMuJdHGN8LHzARAEEiQ1WJYJl4=
161+
github.com/darkweak/storages/simplefs v0.0.11/go.mod h1:FegezzdPj5m3ExDea64WxcDxPz7dFdu8G5WoIaYfbLM=
160162
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
161163
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
162164
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
@@ -384,6 +386,8 @@ github.com/jackc/pgx/v4 v4.18.3/go.mod h1:Ey4Oru5tH5sB6tV7hDmfWFahwF15Eb7DNXlRKx
384386
github.com/jackc/puddle v0.0.0-20190413234325-e4ced69a3a2b/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk=
385387
github.com/jackc/puddle v0.0.0-20190608224051-11cab39313c9/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk=
386388
github.com/jackc/puddle v1.1.3/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk=
389+
github.com/jellydator/ttlcache/v3 v3.3.0 h1:BdoC9cE81qXfrxeb9eoJi9dWrdhSuwXMAnHTbnBm4Wc=
390+
github.com/jellydator/ttlcache/v3 v3.3.0/go.mod h1:bj2/e0l4jRnQdrnSTaGTsh4GSXvMjQcy41i7th0GVGw=
387391
github.com/jessevdk/go-flags v1.4.0/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI=
388392
github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU=
389393
github.com/json-iterator/go v1.1.9/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
@@ -770,8 +774,8 @@ golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJ
770774
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
771775
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
772776
golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
773-
golang.org/x/sync v0.7.0 h1:YsImfSBoP9QPYL0xyKJPq0gcaJdG3rInoqxTWbfQu9M=
774-
golang.org/x/sync v0.7.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
777+
golang.org/x/sync v0.8.0 h1:3NFvSEYkUoMifnESzZl15y791HH1qU2xm6eCJU5ZPXQ=
778+
golang.org/x/sync v0.8.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
775779
golang.org/x/sys v0.0.0-20180823144017-11551d06cbcc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
776780
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
777781
golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=

plugins/caddy/README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -354,6 +354,7 @@ What does these directives mean?
354354
| Key | Description | Value example |
355355
|:------------------------------------------|:---------------------------------------------------------------------------------------------------------------------------------------------|:------------------------------------------------------------------------------------------------------------------------|
356356
| `allowed_http_verbs` | The HTTP verbs allowed to be cached | `GET POST PATCH`<br/><br/>`(default: GET HEAD)` |
357+
| `allowed_additional_status_codes` | The additional HTTP status codes allowed to be cached | `202 400` |
357358
| `api` | The cache-handler API cache management | |
358359
| `api.basepath` | BasePath for all APIs to avoid conflicts | `/your-non-conflict-route`<br/><br/>`(default: /souin-api)` |
359360
| `api.prometheus` | Enable the Prometheus metrics | |

plugins/caddy/configuration.go

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,8 @@ import (
1515
type DefaultCache struct {
1616
// Allowed HTTP verbs to be cached by the system.
1717
AllowedHTTPVerbs []string `json:"allowed_http_verbs"`
18+
// Allowed additional status code to be cached by the system.
19+
AllowedAdditionalStatusCodes []int `json:"allowed_additional_status_codes"`
1820
// Badger provider configuration.
1921
Badger configurationtypes.CacheProvider `json:"badger"`
2022
// The cache name to use in the Cache-Status response header.
@@ -65,6 +67,11 @@ func (d *DefaultCache) GetAllowedHTTPVerbs() []string {
6567
return d.AllowedHTTPVerbs
6668
}
6769

70+
// GetAllowedAdditionalStatusCodes returns the allowed verbs to cache
71+
func (d *DefaultCache) GetAllowedAdditionalStatusCodes() []int {
72+
return d.AllowedAdditionalStatusCodes
73+
}
74+
6875
// GetBadger returns the Badger configuration
6976
func (d *DefaultCache) GetBadger() configurationtypes.CacheProvider {
7077
return d.Badger
@@ -364,6 +371,17 @@ func parseConfiguration(cfg *Configuration, h *caddyfile.Dispenser, isGlobal boo
364371
allowed := cfg.DefaultCache.AllowedHTTPVerbs
365372
allowed = append(allowed, h.RemainingArgs()...)
366373
cfg.DefaultCache.AllowedHTTPVerbs = allowed
374+
case "allowed_additional_status_codes":
375+
allowed := cfg.DefaultCache.AllowedAdditionalStatusCodes
376+
additional := h.RemainingArgs()
377+
codes := make([]int, 0)
378+
for _, code := range additional {
379+
if c, err := strconv.Atoi(code); err == nil {
380+
codes = append(codes, c)
381+
}
382+
}
383+
allowed = append(allowed, codes...)
384+
cfg.DefaultCache.AllowedAdditionalStatusCodes = allowed
367385
case "api":
368386
if !isGlobal {
369387
return h.Err("'api' block must be global")

0 commit comments

Comments
 (0)