@@ -602,6 +602,106 @@ func TestMustRevalidate(t *testing.T) {
602602 }
603603}
604604
605+ type staleIfErrorHandler struct {
606+ iterator int
607+ }
608+
609+ func (t * staleIfErrorHandler ) ServeHTTP (w http.ResponseWriter , r * http.Request ) {
610+ if t .iterator > 0 {
611+ w .WriteHeader (http .StatusInternalServerError )
612+ return
613+ }
614+
615+ t .iterator ++
616+ w .Header ().Set ("Cache-Control" , "stale-if-error=86400" )
617+ w .WriteHeader (http .StatusOK )
618+ _ , _ = w .Write ([]byte ("Hello stale-if-error!" ))
619+ }
620+
621+ func TestStaleIfError (t * testing.T ) {
622+ tester := caddytest .NewTester (t )
623+ tester .InitServer (`
624+ {
625+ admin localhost:2999
626+ http_port 9080
627+ cache {
628+ ttl 5s
629+ stale 5s
630+ }
631+ }
632+ localhost:9080 {
633+ route /stale-if-error {
634+ cache
635+ reverse_proxy localhost:9085
636+ }
637+ }` , "caddyfile" )
638+
639+ go func () {
640+ staleIfErrorHandler := staleIfErrorHandler {}
641+ _ = http .ListenAndServe (":9085" , & staleIfErrorHandler )
642+ }()
643+ time .Sleep (time .Second )
644+ resp1 , _ := tester .AssertGetResponse (`http://localhost:9080/stale-if-error` , http .StatusOK , "Hello stale-if-error!" )
645+ resp2 , _ := tester .AssertGetResponse (`http://localhost:9080/stale-if-error` , http .StatusOK , "Hello stale-if-error!" )
646+
647+ if resp1 .Header .Get ("Cache-Control" ) != "stale-if-error=86400" {
648+ t .Errorf ("unexpected resp1 Cache-Control header %v" , resp1 .Header .Get ("Cache-Control" ))
649+ }
650+ if resp1 .Header .Get ("Cache-Status" ) != "Souin; fwd=uri-miss; stored; key=GET-http-localhost:9080-/stale-if-error" {
651+ t .Errorf ("unexpected resp1 Cache-Status header %v" , resp1 .Header .Get ("Cache-Status" ))
652+ }
653+ if resp1 .Header .Get ("Age" ) != "" {
654+ t .Errorf ("unexpected resp1 Age header %v" , resp1 .Header .Get ("Age" ))
655+ }
656+
657+ if resp2 .Header .Get ("Cache-Control" ) != "stale-if-error=86400" {
658+ t .Errorf ("unexpected resp2 Cache-Control header %v" , resp2 .Header .Get ("Cache-Control" ))
659+ }
660+ if resp2 .Header .Get ("Cache-Status" ) != "Souin; hit; ttl=4; key=GET-http-localhost:9080-/stale-if-error; detail=DEFAULT" {
661+ t .Errorf ("unexpected resp2 Cache-Status header %v" , resp2 .Header .Get ("Cache-Status" ))
662+ }
663+ if resp2 .Header .Get ("Age" ) != "1" {
664+ t .Errorf ("unexpected resp2 Age header %v" , resp2 .Header .Get ("Age" ))
665+ }
666+
667+ time .Sleep (6 * time .Second )
668+ staleReq , _ := http .NewRequest (http .MethodGet , "http://localhost:9080/stale-if-error" , nil )
669+ staleReq .Header = http.Header {"Cache-Control" : []string {"stale-if-error=86400" }}
670+ resp3 , _ := tester .AssertResponse (staleReq , http .StatusOK , "Hello stale-if-error!" )
671+
672+ if resp3 .Header .Get ("Cache-Control" ) != "stale-if-error=86400" {
673+ t .Errorf ("unexpected resp3 Cache-Control header %v" , resp3 .Header .Get ("Cache-Control" ))
674+ }
675+ if resp3 .Header .Get ("Cache-Status" ) != "Souin; hit; ttl=-2; key=GET-http-localhost:9080-/stale-if-error; detail=DEFAULT; fwd=stale; fwd-status=500" {
676+ t .Errorf ("unexpected resp3 Cache-Status header %v" , resp3 .Header .Get ("Cache-Status" ))
677+ }
678+ if resp3 .Header .Get ("Age" ) != "7" {
679+ t .Errorf ("unexpected resp3 Age header %v" , resp3 .Header .Get ("Age" ))
680+ }
681+
682+ resp4 , _ := tester .AssertGetResponse (`http://localhost:9080/stale-if-error` , http .StatusOK , "Hello stale-if-error!" )
683+
684+ if resp4 .Header .Get ("Cache-Status" ) != "Souin; hit; ttl=-2; key=GET-http-localhost:9080-/stale-if-error; detail=DEFAULT; fwd=stale; fwd-status=500" &&
685+ resp4 .Header .Get ("Cache-Status" ) != "Souin; hit; ttl=-3; key=GET-http-localhost:9080-/stale-if-error; detail=DEFAULT; fwd=stale; fwd-status=500" {
686+ t .Errorf ("unexpected resp4 Cache-Status header %v" , resp4 .Header .Get ("Cache-Status" ))
687+ }
688+
689+ if resp4 .Header .Get ("Age" ) != "7" && resp4 .Header .Get ("Age" ) != "8" {
690+ t .Errorf ("unexpected resp4 Age header %v" , resp4 .Header .Get ("Age" ))
691+ }
692+
693+ time .Sleep (6 * time .Second )
694+ resp5 , _ := tester .AssertGetResponse (`http://localhost:9080/stale-if-error` , http .StatusInternalServerError , "" )
695+
696+ if resp5 .Header .Get ("Cache-Status" ) != "Souin; fwd=uri-miss; key=GET-http-localhost:9080-/stale-if-error; detail=UNCACHEABLE-STATUS-CODE" {
697+ t .Errorf ("unexpected resp5 Cache-Status header %v" , resp5 .Header .Get ("Cache-Status" ))
698+ }
699+
700+ if resp5 .Header .Get ("Age" ) != "" {
701+ t .Errorf ("unexpected resp5 Age header %v" , resp5 .Header .Get ("Age" ))
702+ }
703+ }
704+
605705type testETagsHandler struct {}
606706
607707const etagValue = "AAA-BBB"
@@ -1086,3 +1186,61 @@ func TestComplexQuery(t *testing.T) {
10861186 cacheChecker (caddyTester , "fields[]=id&pagination=true" , 9 )
10871187 cacheChecker (caddyTester , "fields[]=id&pagination=false" , 9 )
10881188}
1189+
1190+ func TestBypassWithExpiresAndRevalidate (t * testing.T ) {
1191+ tester := caddytest .NewTester (t )
1192+ tester .InitServer (`
1193+ {
1194+ debug
1195+ admin localhost:2999
1196+ http_port 9080
1197+ https_port 9443
1198+ cache {
1199+ ttl 5s
1200+ stale 5s
1201+ mode bypass
1202+ }
1203+ }
1204+ localhost:9080 {
1205+ route /bypass-with-expires-and-revalidate {
1206+ cache
1207+ header Expires 0
1208+ header Cache-Control "no-store, no-cache, must-revalidate, proxy-revalidate"
1209+ respond "Hello, expires and revalidate!"
1210+ }
1211+ }` , "caddyfile" )
1212+
1213+ respStored1 , _ := tester .AssertGetResponse (`http://localhost:9080/bypass-with-expires-and-revalidate` , 200 , "Hello, expires and revalidate!" )
1214+ if respStored1 .Header .Get ("Cache-Status" ) != "Souin; fwd=uri-miss; stored; key=GET-http-localhost:9080-/bypass-with-expires-and-revalidate" {
1215+ t .Errorf ("unexpected Cache-Status header value %v" , respStored1 .Header .Get ("Cache-Status" ))
1216+ }
1217+ if respStored1 .Header .Get ("Age" ) != "" {
1218+ t .Errorf ("unexpected Age header %v" , respStored1 .Header .Get ("Age" ))
1219+ }
1220+
1221+ respStored2 , _ := tester .AssertGetResponse (`http://localhost:9080/bypass-with-expires-and-revalidate` , 200 , "Hello, expires and revalidate!" )
1222+ if respStored2 .Header .Get ("Cache-Status" ) != "Souin; hit; ttl=4; key=GET-http-localhost:9080-/bypass-with-expires-and-revalidate; detail=DEFAULT" {
1223+ t .Errorf ("unexpected Cache-Status header value %v" , respStored2 .Header .Get ("Cache-Status" ))
1224+ }
1225+ if respStored2 .Header .Get ("Age" ) == "" {
1226+ t .Error ("Age header should be present" )
1227+ }
1228+
1229+ time .Sleep (5 * time .Second )
1230+ respStored3 , _ := tester .AssertGetResponse (`http://localhost:9080/bypass-with-expires-and-revalidate` , 200 , "Hello, expires and revalidate!" )
1231+ if respStored3 .Header .Get ("Cache-Status" ) != "Souin; hit; ttl=-1; key=GET-http-localhost:9080-/bypass-with-expires-and-revalidate; detail=DEFAULT; fwd=stale" {
1232+ t .Errorf ("unexpected Cache-Status header value %v" , respStored3 .Header .Get ("Cache-Status" ))
1233+ }
1234+ if respStored3 .Header .Get ("Age" ) == "" {
1235+ t .Error ("Age header should be present" )
1236+ }
1237+
1238+ time .Sleep (5 * time .Second )
1239+ respStored4 , _ := tester .AssertGetResponse (`http://localhost:9080/bypass-with-expires-and-revalidate` , 200 , "Hello, expires and revalidate!" )
1240+ if respStored4 .Header .Get ("Cache-Status" ) != "Souin; fwd=uri-miss; stored; key=GET-http-localhost:9080-/bypass-with-expires-and-revalidate" {
1241+ t .Errorf ("unexpected Cache-Status header value %v" , respStored4 .Header .Get ("Cache-Status" ))
1242+ }
1243+ if respStored4 .Header .Get ("Age" ) != "" {
1244+ t .Errorf ("unexpected Age header %v" , respStored4 .Header .Get ("Age" ))
1245+ }
1246+ }
0 commit comments