@@ -33,13 +33,16 @@ const (
3333 XETag1 = xEtags + "1"
3434
3535 // XETag2 is the key for the second eTag value.
36+ // Note that in the cache, XETag1 and XETag2 will always be the same.
37+ // In the Response returned from Response, XETag1 will be the cached value (old) and
38+ // XETag2 will be the eTag value from the server (new).
3639 XETag2 = xEtags + "2"
3740)
3841
3942// A Cache interface is used by the Transport to store and retrieve responses.
4043type Cache interface {
4144 // Get returns the []byte representation of a cached response and a bool
42- // set to true if the value isn't empty
45+ // set to set to false if the key is not found or the value is stale.
4346 Get (key string ) (responseBytes []byte , ok bool )
4447 // Set stores the []byte representation of a response against a key
4548 Set (key string , responseBytes []byte )
@@ -65,16 +68,19 @@ func (t *Transport) cacheKey(req *http.Request) string {
6568 }
6669}
6770
68- // cachedResponse returns the cached http.Response for req if present, and nil
69- // otherwise .
70- func (t * Transport ) cachedResponse (req * http.Request ) (resp * http.Response , err error ) {
71+ // cachedResponse returns the cached http.Response for req if present and
72+ // a bool set to false if the value is stale .
73+ func (t * Transport ) cachedResponse (req * http.Request ) (* http.Response , bool , error ) {
7174 cachedVal , ok := t .Cache .Get (t .cacheKey (req ))
72- if ! ok {
73- return
75+ if ! ok && len ( cachedVal ) == 0 {
76+ return nil , false , nil
7477 }
75-
7678 b := bytes .NewBuffer (cachedVal )
77- return http .ReadResponse (bufio .NewReader (b ), req )
79+ resp , err := http .ReadResponse (bufio .NewReader (b ), req )
80+ if err != nil {
81+ return nil , false , err
82+ }
83+ return resp , ok , nil
7884}
7985
8086// Transport is an implementation of http.RoundTripper that will return values from a cache
@@ -145,10 +151,13 @@ func (t *Transport) RoundTrip(req *http.Request) (resp *http.Response, err error
145151
146152 cacheable := cacheKey != ""
147153
148- var cachedResp * http.Response
154+ var (
155+ cachedResp * http.Response
156+ hasCachedResp bool
157+ )
149158 if cacheable {
150- cachedResp , err = t .cachedResponse (req )
151- if err == nil && cachedResp != nil && t .AlwaysUseCachedResponse != nil && t .AlwaysUseCachedResponse (req , cacheKey ) {
159+ cachedResp , hasCachedResp , err = t .cachedResponse (req )
160+ if err == nil && hasCachedResp && t .AlwaysUseCachedResponse != nil && t .AlwaysUseCachedResponse (req , cacheKey ) {
152161 return cachedResp , nil
153162 }
154163 } else {
@@ -161,13 +170,16 @@ func (t *Transport) RoundTrip(req *http.Request) (resp *http.Response, err error
161170 transport = http .DefaultTransport
162171 }
163172
164- if cacheable && cachedResp != nil && err == nil {
165- if t .MarkCachedResponses {
166- cachedResp .Header .Set (XFromCache , "1" )
167- }
173+ if cachedResp != nil {
168174 if t .EnableETagPair {
169175 cachedXEtag , _ = getXETags (cachedResp .Header )
170176 }
177+ }
178+
179+ if cacheable && hasCachedResp && err == nil {
180+ if t .MarkCachedResponses {
181+ cachedResp .Header .Set (XFromCache , "1" )
182+ }
171183
172184 if varyMatches (cachedResp , req ) {
173185 // Can only use cached value if the new request doesn't Vary significantly
@@ -247,16 +259,19 @@ func (t *Transport) RoundTrip(req *http.Request) (resp *http.Response, err error
247259 t .Cache .Set (cacheKey , respBytes )
248260 }
249261 default :
250- var etagHash hash.Hash
262+ var (
263+ etagHash hash.Hash
264+ etag1 = cachedXEtag
265+ etag2 string
266+ )
267+
251268 r := resp .Body
252269 if t .EnableETagPair {
253270 if etag := resp .Header .Get ("etag" ); etag != "" {
254- resp .Header .Set (XETag1 , etag )
255- etag2 := cachedXEtag
271+ etag1 = etag
256272 if etag2 == "" {
257273 etag2 = etag
258274 }
259- resp .Header .Set (XETag2 , etag2 )
260275 } else {
261276 etagHash = md5 .New ()
262277 r = struct {
@@ -274,17 +289,23 @@ func (t *Transport) RoundTrip(req *http.Request) (resp *http.Response, err error
274289 OnEOF : func (r io.Reader ) {
275290 if etagHash != nil {
276291 md5Str := hex .EncodeToString (etagHash .Sum (nil ))
292+ etag2 = md5Str
277293 resp .Header .Set (XETag1 , md5Str )
278- etag2 := cachedXEtag
279- if etag2 == "" {
280- etag2 = md5Str
294+ resp . Header . Set ( XETag2 , md5Str )
295+ if etag1 == "" {
296+ etag1 = md5Str
281297 }
282- resp .Header .Set (XETag2 , etag2 )
298+ } else {
299+ resp .Header .Set (XETag1 , etag1 )
300+ resp .Header .Set (XETag2 , etag1 )
283301 }
302+
284303 resp := * resp
285304 resp .Body = io .NopCloser (r )
286305 respBytes , err := httputil .DumpResponse (& resp , true )
287306 if err == nil {
307+ // Signal any change back to the caller.
308+ resp .Header .Set (XETag1 , etag1 )
288309 t .Cache .Set (cacheKey , respBytes )
289310 }
290311 },
0 commit comments