Skip to content
This repository was archived by the owner on Apr 20, 2023. It is now read-only.

Commit 16db777

Browse files
committed
Merge pull request #52 from gregjones/dont-store-uncacheable-range-responses
Fix invalid caching of uncacheable range requests.
2 parents 37c2ad6 + 4082b08 commit 16db777

File tree

2 files changed

+131
-4
lines changed

2 files changed

+131
-4
lines changed

‎httpcache.go‎

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -180,9 +180,9 @@ func (t *Transport) setModReq(orig, mod *http.Request) {
180180
// will be returned.
181181
func (t *Transport) RoundTrip(req *http.Request) (resp *http.Response, err error) {
182182
cacheKey := cacheKey(req)
183-
cacheableMethod := req.Method == "GET" || req.Method == "HEAD"
183+
cacheable := (req.Method == "GET" || req.Method == "HEAD") && req.Header.Get("range") == ""
184184
var cachedResp *http.Response
185-
if cacheableMethod {
185+
if cacheable {
186186
cachedResp, err = CachedResponse(t.Cache, req)
187187
} else {
188188
// Need to invalidate an existing value
@@ -194,7 +194,7 @@ func (t *Transport) RoundTrip(req *http.Request) (resp *http.Response, err error
194194
transport = http.DefaultTransport
195195
}
196196

197-
if cachedResp != nil && err == nil && cacheableMethod && req.Header.Get("range") == "" {
197+
if cacheable && cachedResp != nil && err == nil {
198198
if t.MarkCachedResponses {
199199
cachedResp.Header.Set(XFromCache, "1")
200200
}
@@ -281,7 +281,7 @@ func (t *Transport) RoundTrip(req *http.Request) (resp *http.Response, err error
281281
}
282282
}
283283

284-
if cacheableMethod && canStore(parseCacheControl(req.Header), parseCacheControl(resp.Header)) {
284+
if cacheable && canStore(parseCacheControl(req.Header), parseCacheControl(resp.Header)) {
285285
for _, varyKey := range headerAllCommaSepValues(resp.Header, "vary") {
286286
varyKey = http.CanonicalHeaderKey(varyKey)
287287
fakeHeader := "X-Varied-" + varyKey

‎httpcache_test.go‎

Lines changed: 127 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,21 @@ func setup() {
5454
w.Write([]byte(r.Method))
5555
}))
5656

57+
mux.HandleFunc("/range", http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
58+
lm := "Fri, 14 Dec 2010 01:01:50 GMT"
59+
if r.Header.Get("if-modified-since") == lm {
60+
w.WriteHeader(http.StatusNotModified)
61+
return
62+
}
63+
w.Header().Set("last-modified", lm)
64+
if r.Header.Get("range") == "bytes=4-9" {
65+
w.WriteHeader(http.StatusPartialContent)
66+
w.Write([]byte(" text "))
67+
return
68+
}
69+
w.Write([]byte("Some text content"))
70+
}))
71+
5772
mux.HandleFunc("/nostore", http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
5873
w.Header().Set("Cache-Control", "no-store")
5974
}))
@@ -184,6 +199,118 @@ func TestCacheableMethod(t *testing.T) {
184199
}
185200
}
186201

202+
func TestDontStorePartialRangeInCache(t *testing.T) {
203+
resetTest()
204+
{
205+
req, err := http.NewRequest("GET", s.server.URL+"/range", nil)
206+
if err != nil {
207+
t.Fatal(err)
208+
}
209+
req.Header.Set("range", "bytes=4-9")
210+
resp, err := s.client.Do(req)
211+
if err != nil {
212+
t.Fatal(err)
213+
}
214+
var buf bytes.Buffer
215+
_, err = io.Copy(&buf, resp.Body)
216+
if err != nil {
217+
t.Fatal(err)
218+
}
219+
err = resp.Body.Close()
220+
if err != nil {
221+
t.Fatal(err)
222+
}
223+
if got, want := buf.String(), " text "; got != want {
224+
t.Errorf("got %q, want %q", got, want)
225+
}
226+
if resp.StatusCode != http.StatusPartialContent {
227+
t.Errorf("response status code isn't 206 Partial Content: %v", resp.StatusCode)
228+
}
229+
}
230+
{
231+
req, err := http.NewRequest("GET", s.server.URL+"/range", nil)
232+
if err != nil {
233+
t.Fatal(err)
234+
}
235+
resp, err := s.client.Do(req)
236+
if err != nil {
237+
t.Fatal(err)
238+
}
239+
var buf bytes.Buffer
240+
_, err = io.Copy(&buf, resp.Body)
241+
if err != nil {
242+
t.Fatal(err)
243+
}
244+
err = resp.Body.Close()
245+
if err != nil {
246+
t.Fatal(err)
247+
}
248+
if got, want := buf.String(), "Some text content"; got != want {
249+
t.Errorf("got %q, want %q", got, want)
250+
}
251+
if resp.StatusCode != http.StatusOK {
252+
t.Errorf("response status code isn't 200 OK: %v", resp.StatusCode)
253+
}
254+
if resp.Header.Get(XFromCache) != "" {
255+
t.Error("XFromCache header isn't blank")
256+
}
257+
}
258+
{
259+
req, err := http.NewRequest("GET", s.server.URL+"/range", nil)
260+
if err != nil {
261+
t.Fatal(err)
262+
}
263+
resp, err := s.client.Do(req)
264+
if err != nil {
265+
t.Fatal(err)
266+
}
267+
var buf bytes.Buffer
268+
_, err = io.Copy(&buf, resp.Body)
269+
if err != nil {
270+
t.Fatal(err)
271+
}
272+
err = resp.Body.Close()
273+
if err != nil {
274+
t.Fatal(err)
275+
}
276+
if got, want := buf.String(), "Some text content"; got != want {
277+
t.Errorf("got %q, want %q", got, want)
278+
}
279+
if resp.StatusCode != http.StatusOK {
280+
t.Errorf("response status code isn't 200 OK: %v", resp.StatusCode)
281+
}
282+
if resp.Header.Get(XFromCache) != "1" {
283+
t.Errorf(`XFromCache header isn't "1": %v`, resp.Header.Get(XFromCache))
284+
}
285+
}
286+
{
287+
req, err := http.NewRequest("GET", s.server.URL+"/range", nil)
288+
if err != nil {
289+
t.Fatal(err)
290+
}
291+
req.Header.Set("range", "bytes=4-9")
292+
resp, err := s.client.Do(req)
293+
if err != nil {
294+
t.Fatal(err)
295+
}
296+
var buf bytes.Buffer
297+
_, err = io.Copy(&buf, resp.Body)
298+
if err != nil {
299+
t.Fatal(err)
300+
}
301+
err = resp.Body.Close()
302+
if err != nil {
303+
t.Fatal(err)
304+
}
305+
if got, want := buf.String(), " text "; got != want {
306+
t.Errorf("got %q, want %q", got, want)
307+
}
308+
if resp.StatusCode != http.StatusPartialContent {
309+
t.Errorf("response status code isn't 206 Partial Content: %v", resp.StatusCode)
310+
}
311+
}
312+
}
313+
187314
func TestGetOnlyIfCachedHit(t *testing.T) {
188315
resetTest()
189316
{

0 commit comments

Comments
 (0)