@@ -16,6 +16,7 @@ import (
1616 "net/http"
1717 "net/http/httputil"
1818 "strings"
19+ "sync"
1920 "time"
2021)
2122
@@ -114,11 +115,36 @@ type Transport struct {
114115 // ShouldCache is an optional func that when it returns false, the response will not be cached.
115116 ShouldCache func (req * http.Request , resp * http.Response , key string ) bool
116117
118+ // CanStore is an optional func that when set, is called to determine if a response
119+ // can be stored in the cache.
120+ // If not set, a default implementation is used that checks for 'no-store' in
121+ // the request and response cache-control headers.
122+ //
123+ // Note that this does not imply that the response will be cached, only that it is
124+ // allowed to be cached. The ShouldCache func is called after this to make the final decision.
125+ CanStore func (reqCacheControl , respCacheControl CacheControl ) (canStore bool )
126+
117127 // Around is an optional func.
118128 // If set, the Transport will call Around at the start of RoundTrip
119129 // and defer the returned func until the end of RoundTrip.
120130 // Typically used to implement a lock that is held for the duration of the RoundTrip.
121131 Around func (req * http.Request , key string ) func ()
132+
133+ init sync.Once
134+ }
135+
136+ func (t * Transport ) doInit () {
137+ if t .Cache == nil {
138+ panic ("no Cache set on Transport" )
139+ }
140+ if t .CanStore == nil {
141+ t .CanStore = canStore
142+ }
143+ if t .ShouldCache == nil {
144+ t .ShouldCache = func (req * http.Request , resp * http.Response , key string ) bool {
145+ return true
146+ }
147+ }
122148}
123149
124150// varyMatches will return false unless all of the cached values for the headers listed in Vary
@@ -142,6 +168,10 @@ func varyMatches(cachedResp *http.Response, req *http.Request) bool {
142168// to give the server a chance to respond with NotModified. If this happens, then the cached Response
143169// will be returned.
144170func (t * Transport ) RoundTrip (req * http.Request ) (resp * http.Response , err error ) {
171+ t .init .Do (func () {
172+ t .doInit ()
173+ })
174+
145175 cacheKey := t .cacheKey (req )
146176 if f := t .Around ; f != nil {
147177 defer f (req , cacheKey )()
@@ -243,7 +273,7 @@ func (t *Transport) RoundTrip(req *http.Request) (resp *http.Response, err error
243273 }
244274 }
245275
246- if cacheable && ( t .ShouldCache == nil || t . ShouldCache (req , resp , cacheKey )) && canStore (parseCacheControl (req .Header ), parseCacheControl (resp .Header )) {
276+ if cacheable && t .ShouldCache (req , resp , cacheKey ) && t . CanStore (parseCacheControl (req .Header ), parseCacheControl (resp .Header )) {
247277 for _ , varyKey := range headerAllCommaSepValues (resp .Header , "vary" ) {
248278 varyKey = http .CanonicalHeaderKey (varyKey )
249279 fakeHeader := "X-Varied-" + varyKey
@@ -513,7 +543,7 @@ func getEndToEndHeaders(respHeaders http.Header) []string {
513543 return endToEndHeaders
514544}
515545
516- func canStore (reqCacheControl , respCacheControl cacheControl ) (canStore bool ) {
546+ func canStore (reqCacheControl , respCacheControl CacheControl ) (canStore bool ) {
517547 if _ , ok := respCacheControl ["no-store" ]; ok {
518548 return false
519549 }
@@ -548,10 +578,11 @@ func cloneRequest(r *http.Request) *http.Request {
548578 return r2
549579}
550580
551- type cacheControl map [string ]string
581+ // CacheControl is a map of the cache-control directives to their values (or "" if no value).
582+ type CacheControl map [string ]string
552583
553- func parseCacheControl (headers http.Header ) cacheControl {
554- cc := cacheControl {}
584+ func parseCacheControl (headers http.Header ) CacheControl {
585+ cc := CacheControl {}
555586 ccHeader := headers .Get ("Cache-Control" )
556587 for _ , part := range strings .Split (ccHeader , "," ) {
557588 part = strings .Trim (part , " " )
0 commit comments