@@ -12,70 +12,102 @@ import (
1212 cachestore "github.com/goware/cachestore2"
1313)
1414
15- // TODO: we want TTL for OK and TTL for error ..
16- // but really, it could be any TTL depending on the request ..
17-
18- // kinda need like ... for status set the TTL ..?
19-
20- func Handler2 (logger * slog.Logger , cacheBackend cachestore.Backend , ttl time.Duration , options ... Option ) func (next http.Handler ) http.Handler {
21- opts := & Options {
22- TTL : ttl ,
23- }
24- for _ , o := range options {
25- o (opts )
26- }
27-
28- var cacheKeyFunc func (r * http.Request ) (uint64 , error )
29-
30- if ! opts .HTTPCacheKeyRequestBody {
31- cacheKeyFunc = func (r * http.Request ) (uint64 , error ) {
32- return StringToHash (strings .ToLower (r .URL .Path )), nil
15+ func HandlerWithKey (logger * slog.Logger , cacheBackend cachestore.Backend , ttl time.Duration , cacheKeyFunc CacheKeyFunc , options ... Option ) func (next http.Handler ) http.Handler {
16+ opts := getOptions (ttl , options ... )
17+
18+ // Combine various cache key functions into a single cache key value.
19+ cacheKeyWithRequestHeaders := cacheKeyWithRequestHeaders (opts .HTTPCacheKeyRequestHeaders )
20+
21+ comboCacheKeyFunc := func (r * http.Request ) (uint64 , error ) {
22+ var cacheKey1 , cacheKey2 , cacheKey3 , cacheKey4 uint64
23+ var err error
24+ cacheKey1 , err = cacheKeyWithRequestURL (r )
25+ if err != nil {
26+ return 0 , err
3327 }
34- } else {
35- cacheKeyFunc = func (r * http.Request ) (uint64 , error ) {
36- // Read the request payload, and then setup buffer for future reader
37- var err error
38- var buf []byte
39- if r .Body != nil {
40- buf , err = io .ReadAll (r .Body )
41- if err != nil {
42- return 0 , err
43- }
44- r .Body = io .NopCloser (bytes .NewBuffer (buf ))
28+ if opts .HTTPCacheKeyRequestBody {
29+ cacheKey2 , err = cacheKeyWithRequestBody (r )
30+ if err != nil {
31+ return 0 , err
4532 }
46-
47- // Prepare cache key based on request URL path and the request data payload.
48- key := BytesToHash ([]byte (strings .ToLower (r .URL .Path )), buf )
49- return key , nil
5033 }
34+ if len (opts .HTTPCacheKeyRequestHeaders ) > 0 {
35+ cacheKey3 , err = cacheKeyWithRequestHeaders (r )
36+ if err != nil {
37+ return 0 , err
38+ }
39+ }
40+ if cacheKeyFunc != nil {
41+ cacheKey4 , err = cacheKeyFunc (r )
42+ if err != nil {
43+ return 0 , err
44+ }
45+ }
46+ return cacheKey1 + cacheKey2 + cacheKey3 + cacheKey4 , nil
5147 }
5248
53- var cache cachestore.Store [responseValue2 ]
49+ var cache cachestore.Store [responseValue ]
5450 if cacheBackend != nil {
55- cache = cachestore.OpenStore [responseValue2 ](cacheBackend )
51+ cache = cachestore.OpenStore [responseValue ](cacheBackend )
5652 }
57- h := stampedeHandler2 (logger , cache , cacheKeyFunc , opts )
53+ h := stampedeHandler (logger , cache , comboCacheKeyFunc , opts )
5854
5955 return func (next http.Handler ) http.Handler {
60-
61- // TODO: the "wee" function(request) doesn't make sense, because
62- // we actually need the response ..
63- // and also, might want to "vary" on the response headers ....?
64-
6556 return http .HandlerFunc (func (w http.ResponseWriter , r * http.Request ) {
6657 h (next ).ServeHTTP (w , r )
6758 })
6859 }
6960}
7061
62+ func Handler (logger * slog.Logger , cacheBackend cachestore.Backend , ttl time.Duration , options ... Option ) func (next http.Handler ) http.Handler {
63+ return HandlerWithKey (logger , cacheBackend , ttl , nil , options ... )
64+ }
65+
66+ func cacheKeyWithRequestURL (r * http.Request ) (uint64 , error ) {
67+ return StringToHash (strings .ToLower (r .URL .Path )), nil
68+ }
69+
70+ func cacheKeyWithRequestBody (r * http.Request ) (uint64 , error ) {
71+ // Read the request payload, and then setup buffer for future reader
72+ var err error
73+ var buf []byte
74+ if r .Body != nil {
75+ buf , err = io .ReadAll (r .Body )
76+ if err != nil {
77+ return 0 , err
78+ }
79+ r .Body = io .NopCloser (bytes .NewBuffer (buf ))
80+ }
81+
82+ // Prepare cache key based on the request data payload.
83+ return BytesToHash (buf ), nil
84+ }
85+
86+ func cacheKeyWithRequestHeaders (headers []string ) func (r * http.Request ) (uint64 , error ) {
87+ return func (r * http.Request ) (uint64 , error ) {
88+ if len (headers ) == 0 {
89+ return 0 , nil
90+ }
91+ var keys []string
92+ for _ , header := range headers {
93+ v := r .Header .Get (header )
94+ if v == "" {
95+ continue
96+ }
97+ keys = append (keys , fmt .Sprintf ("%s:%s" , strings .ToLower (header ), v ))
98+ }
99+ return StringToHash (keys ... ), nil
100+ }
101+ }
102+
71103type CacheKeyFunc func (r * http.Request ) (uint64 , error )
72104
73- func stampedeHandler2 (logger * slog.Logger , cache cachestore.Store [responseValue2 ], cacheKeyFunc CacheKeyFunc , options * Options ) func (next http.Handler ) http.Handler {
105+ func stampedeHandler (logger * slog.Logger , cache cachestore.Store [responseValue ], cacheKeyFunc CacheKeyFunc , options * Options ) func (next http.Handler ) http.Handler {
74106 stampede := NewStampede (cache )
107+ stampede .SetOptions (options )
75108
76109 return func (next http.Handler ) http.Handler {
77110 return http .HandlerFunc (func (w http.ResponseWriter , r * http.Request ) {
78-
79111 cacheKey , err := cacheKeyFunc (r )
80112 if err != nil {
81113 logger .Warn ("stampede: fail to compute cache key" , "err" , err )
@@ -85,23 +117,14 @@ func stampedeHandler2(logger *slog.Logger, cache cachestore.Store[responseValue2
85117
86118 firstRequest := false
87119
88- k := fmt .Sprintf ("%d" , cacheKey ) // TODO ...
89-
90- ttl := options .TTL
91- _ = ttl
92-
93- // TODO: pass down the greater TTL to the .Do()..
94-
95- // TODO: .. THEN .. we can check cachedVal.TS and we'll
96-
97- cachedVal , err := stampede .Do (k , func () (responseValue2 , error ) {
120+ cachedVal , err := stampede .Do (fmt .Sprintf ("%d" , cacheKey ), func () (responseValue , error ) {
98121 firstRequest = true
99122 buf := bytes .NewBuffer (nil )
100123 ww := & responseWriter {ResponseWriter : w , tee : buf }
101124
102125 next .ServeHTTP (ww , r )
103126
104- val := responseValue2 {
127+ val := responseValue {
105128 Headers : ww .Header (),
106129 Status : ww .Status (),
107130 Body : buf .Bytes (),
@@ -111,9 +134,7 @@ func stampedeHandler2(logger *slog.Logger, cache cachestore.Store[responseValue2
111134 Skip : ! ww .IsValid (),
112135 }
113136 return val , nil
114- }) //, options) // TODO .. we need this..
115- _ = cacheKey
116- _ = firstRequest
137+ })
117138
118139 if firstRequest {
119140 fmt .Println ("first request" )
@@ -149,15 +170,15 @@ func stampedeHandler2(logger *slog.Logger, cache cachestore.Store[responseValue2
149170 }
150171 respHeader [k ] = v
151172 }
152- respHeader .Set ("x-cache" , "hit" ) // TODO: confirm works..
173+ respHeader .Set ("x-cache" , "hit" ) // TODO: confirm works....
153174
154175 w .WriteHeader (cachedVal .Status )
155176 w .Write (cachedVal .Body )
156177 })
157178 }
158179}
159180
160- type responseValue2 struct {
181+ type responseValue struct {
161182 Headers http.Header `json:"headers"`
162183 Status int `json:"status"`
163184 Body []byte `json:"body"`
0 commit comments