Skip to content

Commit 1068045

Browse files
committed
Modernise go-cachewrapper.
* This changes the configuration mechanism in favour of functional options. * Updates table-tests not fail-fast. * Switches to using httptest.ResponseRecorder in favour of a custom version.
1 parent 2ec0357 commit 1068045

File tree

4 files changed

+113
-39
lines changed

4 files changed

+113
-39
lines changed

LICENSE

+2-2
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
The MIT License (MIT)
22

3-
Copyright (c) 2014 Kevin
3+
Copyright (c) 2014-2018 Kevin McDermott
44

55
Permission is hereby granted, free of charge, to any person obtaining a copy
66
of this software and associated documentation files (the "Software"), to deal
@@ -18,4 +18,4 @@ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
1818
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
1919
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
2020
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21-
SOFTWARE.
21+
SOFTWARE.

README.md

+4-2
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,8 @@ go-cachewrapper
33

44
Trivial wrapper for http.Handler that applies Cache-Control headers in the style of DropWizard.
55

6+
This uses a range of option functions to configure the cache header pragmas.
7+
68
Example
79
-------
810

@@ -15,7 +17,7 @@ import (
1517
"net/http"
1618
"time"
1719

18-
"github.com/bigkevmcd/go-cachewrapper"
20+
cw "github.com/bigkevmcd/go-cachewrapper"
1921
)
2022

2123
func helloWorld(w http.ResponseWriter, r *http.Request) {
@@ -25,7 +27,7 @@ func helloWorld(w http.ResponseWriter, r *http.Request) {
2527
const cacheTime = time.Minute * 60
2628

2729
func main() {
28-
http.Handle("/", cachewrapper.Cached(http.HandlerFunc(helloWorld), cachewrapper.CacheOptions{MaxAge: cacheTime, NoTransform: true}))
30+
http.Handle("/", cw.Cached(http.HandlerFunc(helloWorld), cw.MaxAge(cacheTime), cw.NoTransform())
2931
log.Fatal(http.ListenAndServe(":8000", nil))
3032
}
3133
```

cache.go

+74-5
Original file line numberDiff line numberDiff line change
@@ -17,10 +17,14 @@ type CacheControl struct {
1717

1818
// Cached returns an http.Handler that sets appropriate Cache headers on
1919
// the outgoing response and passes requests to a wrapped http.Handler.
20-
func Cached(handler http.Handler, o CacheOptions) *CacheControl {
20+
func Cached(handler http.Handler, opts ...optionFunc) *CacheControl {
21+
co := CacheOptions{}
22+
for _, o := range opts {
23+
o(&co)
24+
}
2125
return &CacheControl{
2226
handler: handler,
23-
options: o,
27+
options: co,
2428
}
2529
}
2630

@@ -31,11 +35,74 @@ func (c *CacheControl) ServeHTTP(w http.ResponseWriter, r *http.Request) {
3135
c.handler.ServeHTTP(w, r)
3236
}
3337

34-
// These set the relevant headers in the response per
38+
// MaxAge configures the maximum-age cache option.
39+
func MaxAge(d time.Duration) optionFunc {
40+
return func(o *CacheOptions) {
41+
o.MaxAge = d
42+
}
43+
}
44+
45+
// NoTransform configures the the no-transform pragma.
46+
func NoTransform() optionFunc {
47+
return func(o *CacheOptions) {
48+
o.NoTransform = true
49+
}
50+
}
51+
52+
// Immutable configures the max-age pragma to be one year in the future.
53+
func Immutable() optionFunc {
54+
return func(o *CacheOptions) {
55+
o.Immutable = true
56+
}
57+
}
58+
59+
// Private configures the private cache option.
60+
func Private() optionFunc {
61+
return func(o *CacheOptions) {
62+
o.Private = true
63+
}
64+
}
65+
66+
// NoCache configures the no-cache pragma.
67+
func NoCache() optionFunc {
68+
return func(o *CacheOptions) {
69+
o.NoCache = true
70+
}
71+
}
72+
73+
// NoStore configures the no-store pragma.
74+
func NoStore() optionFunc {
75+
return func(o *CacheOptions) {
76+
o.NoStore = true
77+
}
78+
}
79+
80+
// MustRevalidate configures the must-revalidate pragma.
81+
func MustRevalidate() optionFunc {
82+
return func(o *CacheOptions) {
83+
o.MustRevalidate = true
84+
}
85+
}
86+
87+
// ProxyRevalidate configures the proxy-revalidate pragma.
88+
func ProxyRevalidate() optionFunc {
89+
return func(o *CacheOptions) {
90+
o.ProxyRevalidate = true
91+
}
92+
}
93+
94+
// SharedMaxAge configures the s-maxage pragma.
95+
func SharedMaxAge(d time.Duration) optionFunc {
96+
return func(o *CacheOptions) {
97+
o.SharedMaxAge = d
98+
}
99+
}
100+
101+
// These set the relevant pragmas in the response per
35102
// http://www.w3.org/Protocols/rfc2616/rfc2616-sec13.html
36103
type CacheOptions struct {
37104
Immutable bool
38-
IsPrivate bool
105+
Private bool
39106
NoCache bool
40107
NoStore bool
41108
NoTransform bool
@@ -51,7 +118,7 @@ func (o CacheOptions) String() string {
51118
o.MaxAge = ONE_YEAR_IN_HOURS
52119
}
53120

54-
if o.IsPrivate {
121+
if o.Private {
55122
elements = append(elements, "private")
56123
}
57124

@@ -85,3 +152,5 @@ func (o CacheOptions) String() string {
85152

86153
return strings.Join(elements, ", ")
87154
}
155+
156+
type optionFunc func(o *CacheOptions)

cache_test.go

+33-30
Original file line numberDiff line numberDiff line change
@@ -1,50 +1,29 @@
11
package cachewrapper
22

33
import (
4-
"bytes"
54
"net/http"
5+
"net/http/httptest"
66
"testing"
77
"time"
88
)
99

10-
type testResponseWriter struct {
11-
Body bytes.Buffer
12-
StatusCode int
13-
header http.Header
14-
}
15-
16-
func (w *testResponseWriter) Header() http.Header {
17-
if nil == w.header {
18-
w.header = make(map[string][]string)
19-
}
20-
return w.header
21-
}
22-
23-
func (w *testResponseWriter) Write(p []byte) (int, error) {
24-
return w.Body.Write(p)
25-
}
26-
27-
func (w *testResponseWriter) WriteHeader(code int) {
28-
w.StatusCode = code
29-
}
30-
3110
func TestCacheControl(t *testing.T) {
32-
w := &testResponseWriter{}
11+
w := httptest.NewRecorder()
3312
r, _ := http.NewRequest("POST", "http://example.com/foo", nil)
34-
cached := Cached(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {}), CacheOptions{MaxAge: time.Hour * 24 * 13, NoTransform: true})
13+
cached := Cached(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {}), MaxAge(time.Hour*24*13), NoTransform())
3514
cached.ServeHTTP(w, r)
3615

3716
if "no-transform, max-age=1123200" != w.Header().Get("Cache-Control") {
38-
t.Fatalf("Cache-Control headers were %s, expected 'no-transform, max-age=1123200'", w.Header().Get("Cache-Control"))
17+
t.Fatalf("Cache-Control header: got %s, wanted 'no-transform, max-age=1123200'", w.Header().Get("Cache-Control"))
3918
}
4019
}
4120

42-
var cacheOptions = []struct {
21+
var cacheOptionTests = []struct {
4322
o CacheOptions
4423
h string
4524
}{
4625
{CacheOptions{Immutable: true, NoTransform: true}, "no-transform, max-age=31536000"},
47-
{CacheOptions{IsPrivate: true, NoTransform: true}, "private, no-transform"},
26+
{CacheOptions{Private: true, NoTransform: true}, "private, no-transform"},
4827
{CacheOptions{MaxAge: time.Hour * 24 * 13, NoTransform: true}, "no-transform, max-age=1123200"},
4928
{CacheOptions{NoCache: true, NoTransform: true}, "no-cache, no-transform"},
5029
{CacheOptions{NoStore: true, NoTransform: true}, "no-store, no-transform"},
@@ -55,9 +34,33 @@ var cacheOptions = []struct {
5534
}
5635

5736
func TestCacheOptions(t *testing.T) {
58-
for _, v := range cacheOptions {
59-
if o := v.o.String(); v.h != o {
60-
t.Fatalf("%#v got '%s', expected '%s'", v.o, o, v.h)
37+
for _, tt := range cacheOptionTests {
38+
if o := tt.o.String(); tt.h != o {
39+
t.Errorf("%#v got '%s', wanted '%s'", tt.o, o, tt.h)
40+
}
41+
}
42+
}
43+
44+
var cacheOptionFuncTests = []struct {
45+
f optionFunc
46+
h string
47+
}{
48+
{Immutable(), "max-age=31536000"},
49+
{Private(), "private"},
50+
{MaxAge(time.Hour * 24 * 13), "max-age=1123200"},
51+
{NoCache(), "no-cache"},
52+
{NoStore(), "no-store"},
53+
{MustRevalidate(), "must-revalidate"},
54+
{ProxyRevalidate(), "proxy-revalidate"},
55+
{SharedMaxAge(time.Hour * 13), "s-maxage=46800"},
56+
}
57+
58+
func TestOptionFuncs(t *testing.T) {
59+
for _, tt := range cacheOptionFuncTests {
60+
co := CacheOptions{}
61+
tt.f(&co)
62+
if msg := co.String(); tt.h != msg {
63+
t.Errorf("got '%s', wanted '%s'", msg, tt.h)
6164
}
6265
}
6366
}

0 commit comments

Comments
 (0)