@@ -120,6 +120,17 @@ func setup() {
120120 w .Write ([]byte ("Some text content" ))
121121 }))
122122
123+ mux .HandleFunc ("/cachederror" , http .HandlerFunc (func (w http.ResponseWriter , r * http.Request ) {
124+ etag := "abc"
125+ if r .Header .Get ("if-none-match" ) == etag {
126+ w .WriteHeader (http .StatusNotModified )
127+ return
128+ }
129+ w .Header ().Set ("etag" , etag )
130+ w .WriteHeader (http .StatusNotFound )
131+ w .Write ([]byte ("Not found" ))
132+ }))
133+
123134 updateFieldsCounter := 0
124135 mux .HandleFunc ("/updatefields" , http .HandlerFunc (func (w http.ResponseWriter , r * http.Request ) {
125136 w .Header ().Set ("X-Counter" , strconv .Itoa (updateFieldsCounter ))
@@ -873,6 +884,35 @@ func TestUpdateFields(t *testing.T) {
873884 }
874885}
875886
887+ // This tests the fix for https://github.com/gregjones/httpcache/issues/74.
888+ // Previously, after validating a cached response, its StatusCode
889+ // was incorrectly being replaced.
890+ func TestCachedErrorsKeepStatus (t * testing.T ) {
891+ resetTest ()
892+ req , err := http .NewRequest ("GET" , s .server .URL + "/cachederror" , nil )
893+ if err != nil {
894+ t .Fatal (err )
895+ }
896+ {
897+ resp , err := s .client .Do (req )
898+ if err != nil {
899+ t .Fatal (err )
900+ }
901+ defer resp .Body .Close ()
902+ io .Copy (ioutil .Discard , resp .Body )
903+ }
904+ {
905+ resp , err := s .client .Do (req )
906+ if err != nil {
907+ t .Fatal (err )
908+ }
909+ defer resp .Body .Close ()
910+ if resp .StatusCode != http .StatusNotFound {
911+ t .Fatalf ("Status code isn't 404: %d" , resp .StatusCode )
912+ }
913+ }
914+ }
915+
876916func TestParseCacheControl (t * testing.T ) {
877917 resetTest ()
878918 h := http.Header {}
@@ -1355,6 +1395,57 @@ func TestStaleIfErrorResponseLifetime(t *testing.T) {
13551395 }
13561396}
13571397
1398+ // This tests the fix for https://github.com/gregjones/httpcache/issues/74.
1399+ // Previously, after a stale response was used after encountering an error,
1400+ // its StatusCode was being incorrectly replaced.
1401+ func TestStaleIfErrorKeepsStatus (t * testing.T ) {
1402+ resetTest ()
1403+ now := time .Now ()
1404+ tmock := transportMock {
1405+ response : & http.Response {
1406+ Status : http .StatusText (http .StatusNotFound ),
1407+ StatusCode : http .StatusNotFound ,
1408+ Header : http.Header {
1409+ "Date" : []string {now .Format (time .RFC1123 )},
1410+ "Cache-Control" : []string {"no-cache" },
1411+ },
1412+ Body : ioutil .NopCloser (bytes .NewBuffer ([]byte ("some data" ))),
1413+ },
1414+ err : nil ,
1415+ }
1416+ tp := NewMemoryCacheTransport ()
1417+ tp .Transport = & tmock
1418+
1419+ // First time, response is cached on success
1420+ r , _ := http .NewRequest ("GET" , "http://somewhere.com/" , nil )
1421+ r .Header .Set ("Cache-Control" , "stale-if-error" )
1422+ resp , err := tp .RoundTrip (r )
1423+ if err != nil {
1424+ t .Fatal (err )
1425+ }
1426+ if resp == nil {
1427+ t .Fatal ("resp is nil" )
1428+ }
1429+ _ , err = ioutil .ReadAll (resp .Body )
1430+ if err != nil {
1431+ t .Fatal (err )
1432+ }
1433+
1434+ // On failure, response is returned from the cache
1435+ tmock .response = nil
1436+ tmock .err = errors .New ("some error" )
1437+ resp , err = tp .RoundTrip (r )
1438+ if err != nil {
1439+ t .Fatal (err )
1440+ }
1441+ if resp == nil {
1442+ t .Fatal ("resp is nil" )
1443+ }
1444+ if resp .StatusCode != http .StatusNotFound {
1445+ t .Fatalf ("Status wasn't 404: %d" , resp .StatusCode )
1446+ }
1447+ }
1448+
13581449// Test that http.Client.Timeout is respected when cache transport is used.
13591450// That is so as long as request cancellation is propagated correctly.
13601451// In the past, that required CancelRequest to be implemented correctly,
0 commit comments