diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index a969337..97baa37 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -17,7 +17,7 @@ jobs: # The Windows build currently fail because of https://github.com/golang/go/issues/40795, and because xcaddy isn't compatible with the known workaround #os: [ ubuntu-latest, macos-latest, windows-latest ] os: [ ubuntu-latest, macos-latest ] - go: [ '1.19' ] + go: [ '1.20' ] runs-on: ${{ matrix.os }} diff --git a/go.mod b/go.mod index 31a10b6..dc46626 100644 --- a/go.mod +++ b/go.mod @@ -1,11 +1,11 @@ module github.com/caddyserver/cache-handler -go 1.19 +go 1.20 require ( github.com/buraksezer/olric v0.5.4 github.com/caddyserver/caddy/v2 v2.6.4 - github.com/darkweak/souin v1.6.39 + github.com/darkweak/souin v1.6.40 go.uber.org/zap v1.24.0 ) diff --git a/go.sum b/go.sum index 75d9334..9c2b351 100644 --- a/go.sum +++ b/go.sum @@ -656,8 +656,8 @@ github.com/creack/pty v1.1.7/go.mod h1:lj5s0c3V2DBrqTV7llrYr5NG6My20zk30Fl46Y7Do github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= github.com/darkweak/go-esi v0.0.5 h1:b9LHI8Tz46R+i6p8avKPHAIBRQUCZDebNmKm5w/Zrns= github.com/darkweak/go-esi v0.0.5/go.mod h1:koCJqwum1u6mslyZuq/Phm6hfG1K3ZK5Y7jrUBTH654= -github.com/darkweak/souin v1.6.39 h1:uVO18+pvy5N8dJH+CCfuBD3gx7AXWaBv+RjC9ee1pCk= -github.com/darkweak/souin v1.6.39/go.mod h1:7B7VqPbFrF7AWJDRP6FCbE+zGiRUUqHAmWOs3Q2n4hk= +github.com/darkweak/souin v1.6.40 h1:sjn6jLQNmGnyTsnxtBDP4/6P6E6YeFjRxuiy8EO2Eyg= +github.com/darkweak/souin v1.6.40/go.mod h1:7B7VqPbFrF7AWJDRP6FCbE+zGiRUUqHAmWOs3Q2n4hk= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= diff --git a/httpcache_test.go b/httpcache_test.go index 1eb9a4a..bde0a49 100644 --- a/httpcache_test.go +++ b/httpcache_test.go @@ -2,6 +2,7 @@ package httpcache import ( "net/http" + "strings" "testing" "time" @@ -42,6 +43,41 @@ func TestMinimal(t *testing.T) { } } +func TestHead(t *testing.T) { + tester := caddytest.NewTester(t) + tester.InitServer(` + { + admin localhost:2999 + order cache before rewrite + http_port 9080 + https_port 9443 + cache + } + localhost:9080 { + route /cache-head { + cache + respond "Hello, HEAD!" + } + }`, "caddyfile") + + headReq, _ := http.NewRequest(http.MethodHead, "http://localhost:9080/cache-head", nil) + resp1, _ := tester.AssertResponse(headReq, 200, "") + if resp1.Header.Get("Cache-Status") != "Souin; fwd=uri-miss; stored; key=HEAD-http-localhost:9080-/cache-head" { + t.Errorf("unexpected Cache-Status header %v", resp1.Header) + } + if resp1.Header.Get("Content-Length") != "12" { + t.Errorf("unexpected Content-Length header %v", resp1.Header) + } + + resp2, _ := tester.AssertResponse(headReq, 200, "") + if resp2.Header.Get("Cache-Status") != "Souin; hit; ttl=119; key=HEAD-http-localhost:9080-/cache-head" { + t.Errorf("unexpected Cache-Status header %v", resp2.Header) + } + if resp2.Header.Get("Content-Length") != "12" { + t.Errorf("unexpected Content-Length header %v", resp2.Header) + } +} + func TestQueryString(t *testing.T) { tester := caddytest.NewTester(t) tester.InitServer(` @@ -51,7 +87,7 @@ func TestQueryString(t *testing.T) { http_port 9080 https_port 9443 cache { - key { + key { disable_query } } @@ -59,7 +95,7 @@ func TestQueryString(t *testing.T) { localhost:9080 { route /query-string { cache { - key { + key { disable_query } } @@ -430,3 +466,53 @@ func TestMustRevalidate(t *testing.T) { t.Errorf("unexpected resp5 Age header %v", resp4.Header.Get("Age")) } } + +type testETagsHandler struct{} + +const etagValue = "AAA-BBB" + +func (t *testETagsHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) { + if strings.Contains(r.Header.Get("If-None-Match"), etagValue) { + w.WriteHeader(http.StatusNotModified) + + return + } + w.Header().Set("ETag", etagValue) + w.WriteHeader(http.StatusOK) + _, _ = w.Write([]byte("Hello etag!")) +} + +func Test_ETags(t *testing.T) { + tester := caddytest.NewTester(t) + tester.InitServer(` + { + admin localhost:2999 + order cache before rewrite + http_port 9080 + cache { + ttl 50s + stale 50s + } + } + localhost:9080 { + route /etags { + cache + reverse_proxy localhost:9082 + } + }`, "caddyfile") + + etagsHandler := testETagsHandler{} + go func(eh *testETagsHandler) { + _ = http.ListenAndServe(":9082", eh) + }(&etagsHandler) + _, _ = tester.AssertGetResponse(`http://localhost:9080/etags`, http.StatusOK, "Hello etag!") + staleReq, _ := http.NewRequest(http.MethodGet, "http://localhost:9080/etags", nil) + staleReq.Header = http.Header{"If-None-Match": []string{etagValue}} + _, _ = tester.AssertResponse(staleReq, http.StatusNotModified, "") + staleReq.Header = http.Header{} + _, _ = tester.AssertResponse(staleReq, http.StatusOK, "Hello etag!") + staleReq.Header = http.Header{"If-None-Match": []string{etagValue}} + _, _ = tester.AssertResponse(staleReq, http.StatusNotModified, "") + staleReq.Header = http.Header{"If-None-Match": []string{"other"}} + _, _ = tester.AssertResponse(staleReq, http.StatusOK, "Hello etag!") +}