@@ -2,13 +2,22 @@ package cache
22
33import (
44 "bytes"
5+ "encoding/gob"
6+ "fmt"
57 "net/http"
68 "time"
79
810 "github.com/buildwithgo/amaro"
911)
1012
11- // responseRecorder captures the response status and body for caching.
13+ // CachedResponse stores the response data.
14+ type CachedResponse struct {
15+ StatusCode int
16+ Headers http.Header
17+ Body []byte
18+ }
19+
20+ // responseRecorder captures the response status, headers, and body for caching.
1221type responseRecorder struct {
1322 http.ResponseWriter
1423 statusCode int
@@ -25,25 +34,48 @@ func (r *responseRecorder) Write(b []byte) (int, error) {
2534 return r .ResponseWriter .Write (b )
2635}
2736
28- // CachePage returns a middleware that caches the response body for a given duration.
37+ // KeyGenerator allows customizing the cache key.
38+ type KeyGenerator func (c * amaro.Context ) string
39+
40+ func DefaultKeyGenerator (c * amaro.Context ) string {
41+ return "route_cache:" + c .Request .URL .String ()
42+ }
43+
44+ // CachePage returns a middleware that caches the response for a given duration.
2945// It uses the Cache interface.
30- func CachePage (store Cache , ttl time.Duration ) amaro.Middleware {
46+ func CachePage (store Cache , ttl time.Duration , keyGen ... KeyGenerator ) amaro.Middleware {
47+ getKey := DefaultKeyGenerator
48+ if len (keyGen ) > 0 {
49+ getKey = keyGen [0 ]
50+ }
51+
3152 return func (next amaro.Handler ) amaro.Handler {
3253 return func (c * amaro.Context ) error {
3354 // Only cache GET requests
3455 if c .Request .Method != http .MethodGet {
3556 return next (c )
3657 }
3758
38- key := "route_cache:" + c . Request . URL . String ( )
59+ key := getKey ( c )
3960
4061 // Check cache
4162 if val , ok := store .Get (key ); ok {
42- // Hit - We must assert to []byte
43- if bodyBytes , ok := val .([]byte ); ok {
44- c .Writer .Header ().Set ("X-Cache" , "HIT" )
45- c .Writer .Write (bodyBytes )
46- return nil
63+ if cachedBytes , ok := val .([]byte ); ok {
64+ var cached CachedResponse
65+ // Use Gob for simple serialization of struct with headers
66+ buf := bytes .NewBuffer (cachedBytes )
67+ if err := gob .NewDecoder (buf ).Decode (& cached ); err == nil {
68+ // Replay headers
69+ for k , v := range cached .Headers {
70+ for _ , h := range v {
71+ c .Writer .Header ().Add (k , h )
72+ }
73+ }
74+ c .Writer .Header ().Set ("X-Cache" , "HIT" )
75+ c .Writer .WriteHeader (cached .StatusCode )
76+ c .Writer .Write (cached .Body )
77+ return nil
78+ }
4779 }
4880 }
4981
@@ -59,8 +91,20 @@ func CachePage(store Cache, ttl time.Duration) amaro.Middleware {
5991 err := next (c )
6092
6193 // If successful, cache the result
62- if err == nil && recorder .statusCode == http .StatusOK {
63- store .Set (key , recorder .body .Bytes (), ttl )
94+ if err == nil && recorder .statusCode < 400 {
95+ // Create cached response
96+ resp := CachedResponse {
97+ StatusCode : recorder .statusCode ,
98+ Headers : recorder .Header ().Clone (), // Copy headers
99+ Body : recorder .body .Bytes (),
100+ }
101+
102+ var buf bytes.Buffer
103+ if err := gob .NewEncoder (& buf ).Encode (resp ); err == nil {
104+ store .Set (key , buf .Bytes (), ttl )
105+ } else {
106+ fmt .Println ("Cache encode error:" , err )
107+ }
64108 }
65109
66110 return err
0 commit comments