@@ -283,12 +283,8 @@ func (r *transport) handleCacheHit(
283283 return r .serveFromCache (stored , freshness , isRespNoCacheQualified , respNoCacheFieldsSeq )
284284 }
285285
286- if freshness .IsStale && ccResp .MustRevalidate () {
287- goto revalidate
288- }
289-
290- // Unqualified no-cache: must revalidate before serving from cache
291- if hasRespNoCache && ! isRespNoCacheQualified {
286+ if (freshness .IsStale && ccResp .MustRevalidate ()) ||
287+ (hasRespNoCache && ! isRespNoCacheQualified ) { // Unqualified no-cache: must revalidate before serving from cache
292288 goto revalidate
293289 }
294290
@@ -337,6 +333,8 @@ func (r *transport) serveFromCache(
337333 return stored .Data , nil
338334}
339335
336+ // handleStaleWhileRevalidate serves a stale cached response immediately and triggers
337+ // background revalidation in a separate goroutine (RFC 5861, §3).
340338func (r * transport ) handleStaleWhileRevalidate (
341339 req * http.Request ,
342340 stored * internal.Response ,
@@ -346,6 +344,12 @@ func (r *transport) handleStaleWhileRevalidate(
346344) (* http.Response , error ) {
347345 req2 := req .Clone (req .Context ())
348346 req2 = withConditionalHeaders (req2 , stored .Data .Header )
347+ // Background revalidation is "best effort"; it is not guaranteed to complete
348+ // if the program exits before the goroutine finishes. This design choice was
349+ // made to keep the API simple and avoid requiring explicit shutdown coordination.
350+ //
351+ // Open a discussion at github.com/bartventer/httpcache/issues if your use case requires
352+ // guaranteed completion.
349353 go r .performBackgroundRevalidation (req2 , stored , urlKey , freshness , ccReq )
350354 internal .CacheStatusStale .ApplyTo (stored .Data .Header )
351355 return stored .Data , nil
0 commit comments