Skip to content

Commit a278963

Browse files
authored
Implement statusCodeFilter option for client (#2)
See [pull request](victorspringer/http-cache#13). Co-authored-by: Elliot <elliotlunness@lazercube.com>
1 parent a5b6397 commit a278963

4 files changed

Lines changed: 90 additions & 8 deletions

File tree

LICENSE

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
MIT License
22

3+
Copyright (c) 2022 Elliot Lunness
34
Copyright (c) 2018 Victor Springer
45

56
Permission is hereby granted, free of charge, to any person obtaining a copy

README.md

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,15 @@ It is simple, super fast, thread safe and gives the possibility to choose the ad
1111

1212
The memory adapter minimizes GC overhead to near zero and supports some options of caching algorithms (LRU, MRU, LFU, MFU). This way, it is able to store plenty of gigabytes of responses, keeping great performance and being free of leaks.
1313

14-
**Note:** Some tests are currently disabled as they weren't updated when the library was updated for use with echo.
14+
**Note:** Some tests are currently disabled as they weren't updated when the library was updated for use with echo. I plan to fix this soon.
15+
16+
## Original Credit
17+
18+
Project has been detached from the original repository as it's not maintained anymore and github defaults PRs to the original repository.
19+
20+
* [echo-http-cache](https://github.com/SporkHubr/echo-http-cache)
21+
* [http-cache](https://github.com/victorspringer/http-cache)
22+
1523

1624
## Getting Started
1725

@@ -84,6 +92,7 @@ import (
8492
cache.ClientWithAdapter(redis.NewAdapter(ringOpt)),
8593
cache.ClientWithTTL(10 * time.Minute),
8694
cache.ClientWithRefreshKey("opn"),
95+
cache.ClientWithStatusCodeFilter(func(code int) bool { return code != 400 }), // Default
8796
)
8897

8998
...

cache.go

Lines changed: 23 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,9 @@ type Response struct {
5252
// Header is the cached response header.
5353
Header http.Header
5454

55+
// StatusCode is the cached response status code.
56+
StatusCode int
57+
5558
// Expiration is the cached response expiration date.
5659
Expiration time.Time
5760

@@ -66,11 +69,12 @@ type Response struct {
6669

6770
// Client data structure for HTTP cache middleware.
6871
type Client struct {
69-
adapter Adapter
70-
ttl time.Duration
71-
refreshKey string
72-
methods []string
73-
restrictedPaths []string
72+
adapter Adapter
73+
ttl time.Duration
74+
refreshKey string
75+
methods []string
76+
restrictedPaths []string
77+
statusCodeFilter func(int) bool
7478
}
7579

7680
type bodyDumpResponseWriter struct {
@@ -152,10 +156,10 @@ func (client *Client) Middleware() echo.MiddlewareFunc {
152156
response.Frequency++
153157
client.adapter.Set(key, response.Bytes(), response.Expiration)
154158

155-
//w.WriteHeader(http.StatusNotModified)
156159
for k, v := range response.Header {
157160
c.Response().Header().Set(k, strings.Join(v, ","))
158161
}
162+
c.Response().WriteHeader(response.StatusCode)
159163
c.Response().WriteHeader(http.StatusOK)
160164
c.Response().Write(response.Value)
161165
return nil
@@ -175,7 +179,7 @@ func (client *Client) Middleware() echo.MiddlewareFunc {
175179

176180
statusCode := writer.statusCode
177181
value := resBody.Bytes()
178-
if statusCode < 400 {
182+
if client.statusCodeFilter(statusCode) {
179183
now := time.Now()
180184

181185
response := Response{
@@ -288,10 +292,22 @@ func NewClient(opts ...ClientOption) (*Client, error) {
288292
if c.methods == nil {
289293
c.methods = []string{http.MethodGet}
290294
}
295+
if c.statusCodeFilter == nil {
296+
c.statusCodeFilter = func(code int) bool { return code < 400 }
297+
}
291298

292299
return c, nil
293300
}
294301

302+
// ClientWithStatusCodeFilter sets the acceptable status codes to be cached.
303+
// Optional setting. If not set, default filter allows caching of every response with status code below 400.
304+
func ClientWithStatusCodeFilter(filter func(int) bool) ClientOption {
305+
return func(c *Client) error {
306+
c.statusCodeFilter = filter
307+
return nil
308+
}
309+
}
310+
295311
// ClientWithAdapter sets the adapter type for the HTTP cache
296312
// middleware client.
297313
func ClientWithAdapter(a Adapter) ClientOption {

cache_test.go

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,18 +52,22 @@ func (errReader) Read(p []byte) (n int, err error) {
5252
// store: map[uint64][]byte{
5353
// 14974843192121052621: Response{
5454
// Value: []byte("value 1"),
55+
// StatusCode: 200,
5556
// Expiration: time.Now().Add(1 * time.Minute),
5657
// }.Bytes(),
5758
// 14974839893586167988: Response{
5859
// Value: []byte("value 2"),
60+
// StatusCode: 200,
5961
// Expiration: time.Now().Add(1 * time.Minute),
6062
// }.Bytes(),
6163
// 14974840993097796199: Response{
6264
// Value: []byte("value 3"),
65+
// StatusCode: 200,
6366
// Expiration: time.Now().Add(-1 * time.Minute),
6467
// }.Bytes(),
6568
// 10956846073361780255: Response{
6669
// Value: []byte("value 4"),
70+
// StatusCode: 200,
6771
// Expiration: time.Now().Add(-1 * time.Minute),
6872
// }.Bytes(),
6973
// },
@@ -470,9 +474,61 @@ func TestNewClient(t *testing.T) {
470474
t.Errorf("NewClient() error = %v, wantErr %v", err, tt.wantErr)
471475
return
472476
}
477+
if tt.want != nil {
478+
got.statusCodeFilter = nil
479+
tt.want.statusCodeFilter = nil
480+
}
473481
if !reflect.DeepEqual(got, tt.want) {
474482
t.Errorf("NewClient() = %v, want %v", got, tt.want)
475483
}
476484
})
477485
}
478486
}
487+
488+
func TestNewClientWithStatusCodeFilter(t *testing.T) {
489+
adapter := &adapterMock{}
490+
491+
tests := []struct {
492+
name string
493+
opts []ClientOption
494+
wantCache []int
495+
wantSkip []int
496+
}{
497+
{
498+
"returns new client with status code filter",
499+
[]ClientOption{
500+
ClientWithAdapter(adapter),
501+
ClientWithTTL(1 * time.Millisecond),
502+
},
503+
[]int{200, 300},
504+
[]int{400, 500},
505+
},
506+
{
507+
"returns new client with status code filter",
508+
[]ClientOption{
509+
ClientWithAdapter(adapter),
510+
ClientWithTTL(1 * time.Millisecond),
511+
ClientWithStatusCodeFilter(func(code int) bool { return code < 350 || code > 450 }),
512+
},
513+
[]int{200, 300, 500},
514+
[]int{400},
515+
},
516+
}
517+
for _, tt := range tests {
518+
t.Run(tt.name, func(t *testing.T) {
519+
got, _ := NewClient(tt.opts...)
520+
521+
for _, c := range tt.wantCache {
522+
if got.statusCodeFilter(c) == false {
523+
t.Errorf("NewClient() allows caching of status code %v, don't want it to", c)
524+
}
525+
}
526+
527+
for _, c := range tt.wantSkip {
528+
if got.statusCodeFilter(c) == true {
529+
t.Errorf("NewClient() doesn't allow caching of status code %v, want it to", c)
530+
}
531+
}
532+
})
533+
}
534+
}

0 commit comments

Comments
 (0)