blob: e4314b893c1f372bb887d03e5c7d782e38d4407f [file] [log] [blame]
package rfc
/*
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
// Package rfc contains functions implementing RFC 7234, 2616, and other RFCs.
// When changing functions, be sure they still conform to the corresponding RFC.
// When adding symbols, document the RFC and section they correspond to.
import (
"net/http"
"os"
"testing"
"time"
"github.com/apache/trafficcontrol/grove/remapdata"
"github.com/apache/trafficcontrol/grove/web"
"github.com/apache/trafficcontrol/lib/go-log"
)
func TestRules(t *testing.T) {
// test client no-store is obeyed with strict RFC - tests RFC7234§5.2.1.5 compliance
{
reqHdr := http.Header{
"Cache-Control": {"no-store"},
}
respCode := 200
respHdr := http.Header{}
strictRFC := true
if CanCache(http.MethodGet, reqHdr, respCode, respHdr, strictRFC) {
t.Errorf("CanCache returned true for no-store request and strict RFC")
}
}
// test client no-store is ignored without strict RFC - tests RFC7234§5.2.1.5 violation to protect origins
{
reqHdr := http.Header{
"Cache-Control": {"no-store"},
}
respCode := 200
respHdr := http.Header{}
strictRFC := false
if !CanCache(http.MethodGet, reqHdr, respCode, respHdr, strictRFC) {
t.Errorf("CanCache returned false for no-store request and strict RFC disabled")
}
}
// test client no-cache no-store is ignored without strict RFC - tests RFC7234§5.2.1.5 violation to protect origins
{
reqHdr := http.Header{
"Cache-Control": {"no-cache no-store"},
}
respCode := 200
respHdr := http.Header{}
strictRFC := false
if !CanCache(http.MethodGet, reqHdr, respCode, respHdr, strictRFC) {
t.Errorf("CanCache returned false for no-store request and strict RFC disabled")
}
}
// test client no-cache is ignored without strict RFC - tests RFC7234§5.2.1.4 violation to protect origins
{
reqHdr := http.Header{
"Cache-Control": {"no-cache"},
}
respHdr := http.Header{}
reqCC := web.CacheControl{
"no-cache": "",
}
respCC := web.CacheControl{}
respReqHdrs := http.Header{}
respReqTime := time.Now()
respRespTime := time.Now()
strictRFC := false
if reuse := CanReuseStored(reqHdr, respHdr, reqCC, respCC, respReqHdrs, respReqTime, respRespTime, strictRFC); reuse != remapdata.ReuseCan {
t.Errorf("CanReuseStored for no-cache request and strict RFC disabled: expected ReuseCan, actual %v", reuse)
}
}
// test client no-store is ignored without strict RFC - tests RFC7234§5.2.1.4 violation to protect origins
{
reqHdr := http.Header{
"Cache-Control": {"no-store no-cache"},
}
respHdr := http.Header{}
reqCC := web.CacheControl{
"no-store": "",
"no-cache": "",
}
respCC := web.CacheControl{}
respReqHdrs := http.Header{}
respReqTime := time.Now()
respRespTime := time.Now()
strictRFC := false
if reuse := CanReuseStored(reqHdr, respHdr, reqCC, respCC, respReqHdrs, respReqTime, respRespTime, strictRFC); reuse != remapdata.ReuseCan {
t.Errorf("CanReuseStored for no-cache request and strict RFC disabled: expected ReuseCan, actual %v", reuse)
}
}
// test client no-cache and no-store is ignored without strict RFC - tests RFC7234§5.2.1.4 violation to protect origins
{
reqHdr := http.Header{
"Cache-Control": {"no-store no-cache"},
}
respHdr := http.Header{}
reqCC := web.CacheControl{
"no-store": "",
"no-cache": "",
}
respCC := web.CacheControl{}
respReqHdrs := http.Header{}
respReqTime := time.Now()
respRespTime := time.Now()
strictRFC := false
if reuse := CanReuseStored(reqHdr, respHdr, reqCC, respCC, respReqHdrs, respReqTime, respRespTime, strictRFC); reuse != remapdata.ReuseCan {
t.Errorf("CanReuseStored for no-cache request and strict RFC disabled: expected ReuseCan, actual %v", reuse)
}
}
// test parent no-cache is obeyed with strict RFC
{
reqHdr := http.Header{}
respCode := 200
respHdr := http.Header{
"Cache-Control": {"no-cache"},
}
strictRFC := false
if CanCache(http.MethodGet, reqHdr, respCode, respHdr, strictRFC) {
t.Errorf("CanCache returned true for no-cache request and strict RFC disabled")
}
}
// test parent no-store is obeyed with strict RFC
{
reqHdr := http.Header{}
respCode := 200
respHdr := http.Header{
"Cache-Control": {"no-store"},
}
strictRFC := false
if CanCache(http.MethodGet, reqHdr, respCode, respHdr, strictRFC) {
t.Errorf("CanCache returned true for no-cache request and strict RFC disabled")
}
}
// test parent no-cache is obeyed without strict RFC
{
reqHdr := http.Header{}
respCode := 200
respHdr := http.Header{
"Cache-Control": {"no-cache"},
}
strictRFC := false
if CanCache(http.MethodGet, reqHdr, respCode, respHdr, strictRFC) {
t.Errorf("CanCache returned true for no-cache request and strict RFC enabled")
}
}
// test parent no-store is obeyed without strict RFC
{
reqHdr := http.Header{}
respCode := 200
respHdr := http.Header{
"Cache-Control": {"no-store"},
}
strictRFC := true
if CanCache(http.MethodGet, reqHdr, respCode, respHdr, strictRFC) {
t.Errorf("CanCache returned true for no-cache request and strict RFC enabled")
}
}
// test parent Expires in future is reused
{
now := time.Now()
tenMinsBeforeExpires := now.Add(time.Minute * -10)
expires := now.Format(time.RFC1123)
reqHdr := http.Header{}
respHdr := http.Header{
"Expires": {expires},
}
reqCC := web.CacheControl{}
respCC := web.CacheControl{}
respReqHdrs := http.Header{}
respReqTime := tenMinsBeforeExpires
respRespTime := tenMinsBeforeExpires
strictRFC := false
if reuse := CanReuseStored(reqHdr, respHdr, reqCC, respCC, respReqHdrs, respReqTime, respRespTime, strictRFC); reuse != remapdata.ReuseCan {
t.Errorf("CanReuseStored request after expires: expected ReuseCan, actual %v", reuse)
}
}
// test parent Expires in past has revaldiate and can stale
{
now := time.Now()
tenMinutesAgo := now.Add(time.Minute * -10)
expires := tenMinutesAgo.Format(time.RFC1123)
reqHdr := http.Header{}
respHdr := http.Header{
"Expires": {expires},
"Date": {expires},
}
reqCC := web.CacheControl{}
respCC := web.CacheControl{}
respReqHdrs := http.Header{}
respReqTime := now
respRespTime := now
strictRFC := false
if reuse := CanReuseStored(reqHdr, respHdr, reqCC, respCC, respReqHdrs, respReqTime, respRespTime, strictRFC); reuse != remapdata.ReuseMustRevalidateCanStale {
t.Errorf("CanReuseStored request after expires: expected ReuseMustRevalidateCanStale, actual %v", reuse)
}
}
// test parent Expires in past with must-revaldiate, cannot stale
{
now := time.Now()
tenMinutesAgo := now.Add(time.Minute * -10)
expires := tenMinutesAgo.Format(time.RFC1123)
reqHdr := http.Header{}
respHdr := http.Header{
"Expires": {expires},
"Date": {expires},
"Cache-Control": {"must-revalidate"},
}
reqCC := web.CacheControl{}
respCC := web.CacheControl{
"must-revalidate": ""}
respReqHdrs := http.Header{}
respReqTime := now
respRespTime := now
strictRFC := false
if reuse := CanReuseStored(reqHdr, respHdr, reqCC, respCC, respReqHdrs, respReqTime, respRespTime, strictRFC); reuse != remapdata.ReuseMustRevalidate {
t.Errorf("CanReuseStored request after expires and response must-revalidate: expected ReuseMustRevalidate, actual %v", reuse)
}
}
// test parent Expires in past proxy-revaldiate, and no-stale
{
now := time.Now()
tenMinutesAgo := now.Add(time.Minute * -10)
expires := tenMinutesAgo.Format(time.RFC1123)
reqHdr := http.Header{}
respHdr := http.Header{
"Expires": {expires},
"Date": {expires},
"Cache-Control": {"must-revalidate"},
}
reqCC := web.CacheControl{}
respCC := web.CacheControl{
"must-revalidate": ""}
respReqHdrs := http.Header{}
respReqTime := now
respRespTime := now
strictRFC := false
if reuse := CanReuseStored(reqHdr, respHdr, reqCC, respCC, respReqHdrs, respReqTime, respRespTime, strictRFC); reuse != remapdata.ReuseMustRevalidate {
t.Errorf("CanReuseStored request after expires and response must-revalidate: expected ReuseMustRevalidate, actual %v", reuse)
}
}
// test parent Expires in past with proxy-revaldiate returns MustRevalidateNoStale
{
now := time.Now()
tenMinutesAgo := now.Add(time.Minute * -10)
expires := tenMinutesAgo.Format(time.RFC1123)
reqHdr := http.Header{}
respHdr := http.Header{
"Expires": {expires},
"Date": {expires},
"Cache-Control": {"proxy-revalidate"},
}
reqCC := web.CacheControl{}
respCC := web.CacheControl{
"proxy-revalidate": ""}
respReqHdrs := http.Header{}
respReqTime := now
respRespTime := now
strictRFC := false
if reuse := CanReuseStored(reqHdr, respHdr, reqCC, respCC, respReqHdrs, respReqTime, respRespTime, strictRFC); reuse != remapdata.ReuseMustRevalidate {
t.Errorf("CanReuseStored request after expires and response must-revalidate: expected ReuseMustRevalidate, actual %v", reuse)
}
}
// test parent max-age in future returns CanReuse
{
now := time.Now()
tenMinutesAgo := now.Add(time.Minute * -10)
reqHdr := http.Header{}
respHdr := http.Header{
"Date": {tenMinutesAgo.Format(time.RFC1123)},
"Cache-Control": {"max-age=1200"}, // 20 minutes
}
reqCC := web.CacheControl{}
respCC := web.CacheControl{
"max-age": "1200"}
respReqHdrs := http.Header{}
respReqTime := now
respRespTime := now
strictRFC := false
if reuse := CanReuseStored(reqHdr, respHdr, reqCC, respCC, respReqHdrs, respReqTime, respRespTime, strictRFC); reuse != remapdata.ReuseCan {
t.Errorf("CanReuseStored request after expires and response must-revalidate: expected ReuseCan, actual %v", reuse)
}
}
// test parent max-age in past returns MustRevalidate
{
now := time.Now()
tenMinutesAgo := now.Add(time.Minute * -10)
reqHdr := http.Header{}
respHdr := http.Header{
"Date": {tenMinutesAgo.Format(time.RFC1123)},
"Cache-Control": {"max-age=300"}, // 5 minutes
}
reqCC := web.CacheControl{}
respCC := web.CacheControl{
"max-age": "300"}
respReqHdrs := http.Header{}
respReqTime := tenMinutesAgo
respRespTime := tenMinutesAgo
strictRFC := false
if reuse := CanReuseStored(reqHdr, respHdr, reqCC, respCC, respReqHdrs, respReqTime, respRespTime, strictRFC); reuse != remapdata.ReuseMustRevalidateCanStale {
t.Errorf("CanReuseStored request after response max-age: expected ReuseMustRevalidateCanStale, actual %v", reuse)
}
}
// test parent s-maxage in future returns CanReuse
{
now := time.Now()
tenMinutesAgo := now.Add(time.Minute * -10)
reqHdr := http.Header{}
respHdr := http.Header{
"Date": {tenMinutesAgo.Format(time.RFC1123)},
"Cache-Control": {"s-maxage=1200"}, // 20 minutes
}
reqCC := web.CacheControl{}
respCC := web.CacheControl{
"s-maxage": "1200"}
respReqHdrs := http.Header{}
respReqTime := now
respRespTime := now
strictRFC := false
if reuse := CanReuseStored(reqHdr, respHdr, reqCC, respCC, respReqHdrs, respReqTime, respRespTime, strictRFC); reuse != remapdata.ReuseCan {
t.Errorf("CanReuseStored request before s-maxage: expected ReuseCan, actual %v", reuse)
}
}
// test parent s-age in past returns MustRevalidate
{
now := time.Now()
tenMinutesAgo := now.Add(time.Minute * -10)
reqHdr := http.Header{}
respHdr := http.Header{
"Date": {tenMinutesAgo.Format(time.RFC1123)},
"Cache-Control": {"s-maxage=300"}, // 5 minutes
}
reqCC := web.CacheControl{}
respCC := web.CacheControl{
"s-maxage": "300"}
respReqHdrs := http.Header{}
respReqTime := tenMinutesAgo
respRespTime := tenMinutesAgo
strictRFC := false
if reuse := CanReuseStored(reqHdr, respHdr, reqCC, respCC, respReqHdrs, respReqTime, respRespTime, strictRFC); reuse != remapdata.ReuseMustRevalidateCanStale {
t.Errorf("CanReuseStored request after response s-maxage: expected ReuseMustRevalidateCanStale, actual %v", reuse)
}
}
// test parent future s-maxage overrides past max-age and returns CanReuse
{
now := time.Now()
tenMinutesAgo := now.Add(time.Minute * -10)
reqHdr := http.Header{}
respHdr := http.Header{
"Date": {tenMinutesAgo.Format(time.RFC1123)},
"Cache-Control": {"max-age=300,s-maxage=1200"},
}
reqCC := web.CacheControl{}
respCC := web.CacheControl{
"s-maxage": "1200",
"max-age": "300",
}
respReqHdrs := http.Header{}
respReqTime := tenMinutesAgo
respRespTime := tenMinutesAgo
strictRFC := false
if reuse := CanReuseStored(reqHdr, respHdr, reqCC, respCC, respReqHdrs, respReqTime, respRespTime, strictRFC); reuse != remapdata.ReuseCan {
t.Errorf("CanReuseStored request before s-maxage but after max-age: expected ReuseCan, actual %v", reuse)
}
}
// test parent past s-maxage overrides future max-age and returns MustReval
{
now := time.Now()
tenMinutesAgo := now.Add(time.Minute * -10)
reqHdr := http.Header{}
respHdr := http.Header{
"Date": {tenMinutesAgo.Format(time.RFC1123)},
"Cache-Control": {"max-age=1200,s-maxage=300"},
}
reqCC := web.CacheControl{}
respCC := web.CacheControl{
"s-maxage": "300",
"max-age": "1200",
}
respReqHdrs := http.Header{}
respReqTime := tenMinutesAgo
respRespTime := tenMinutesAgo
strictRFC := false
if reuse := CanReuseStored(reqHdr, respHdr, reqCC, respCC, respReqHdrs, respReqTime, respRespTime, strictRFC); reuse != remapdata.ReuseMustRevalidateCanStale {
t.Errorf("CanReuseStored request before s-maxage but after max-age: expected ReuseMustRevalidateCanStale, actual %v", reuse)
}
}
// test parent future max-age overrides past Expires and returns CanReuse
{
now := time.Now()
twentyMinutesAgo := now.Add(time.Minute * -10)
tenMinutesAgo := now.Add(time.Minute * -10)
expires := twentyMinutesAgo.Format(time.RFC1123)
reqHdr := http.Header{}
respHdr := http.Header{
"Date": {tenMinutesAgo.Format(time.RFC1123)},
"Expires": {expires},
"Cache-Control": {"max-age=1200"},
}
reqCC := web.CacheControl{}
respCC := web.CacheControl{
"max-age": "1200",
}
respReqHdrs := http.Header{}
respReqTime := tenMinutesAgo
respRespTime := tenMinutesAgo
strictRFC := false
if reuse := CanReuseStored(reqHdr, respHdr, reqCC, respCC, respReqHdrs, respReqTime, respRespTime, strictRFC); reuse != remapdata.ReuseCan {
t.Errorf("CanReuseStored request before max-age but after Expires: expected ReuseCan, actual %v", reuse)
}
}
// test parent past max-age overrides future Expires and returns MustRevalidate
{
now := time.Now()
tenMinutesAgo := now.Add(time.Minute * -10)
fiveMinutesHence := now.Add(time.Minute * 5)
expires := fiveMinutesHence.Format(time.RFC1123)
reqHdr := http.Header{}
respHdr := http.Header{
"Date": {tenMinutesAgo.Format(time.RFC1123)},
"Expires": {expires},
"Cache-Control": {"max-age=300"},
}
reqCC := web.CacheControl{}
respCC := web.CacheControl{
"max-age": "300",
}
respReqHdrs := http.Header{}
respReqTime := tenMinutesAgo
respRespTime := tenMinutesAgo
strictRFC := false
if reuse := CanReuseStored(reqHdr, respHdr, reqCC, respCC, respReqHdrs, respReqTime, respRespTime, strictRFC); reuse != remapdata.ReuseMustRevalidateCanStale {
t.Errorf("CanReuseStored request after max-age but before Expires: expected ReuseMustRevalidateCanStale, actual %v", reuse)
}
}
// test parent future s-maxage overrides past Expires and returns CanReuse
{
now := time.Now()
twentyMinutesAgo := now.Add(time.Minute * -10)
tenMinutesAgo := now.Add(time.Minute * -10)
expires := twentyMinutesAgo.Format(time.RFC1123)
reqHdr := http.Header{}
respHdr := http.Header{
"Date": {tenMinutesAgo.Format(time.RFC1123)},
"Expires": {expires},
"Cache-Control": {"s-maxage=1200"},
}
reqCC := web.CacheControl{}
respCC := web.CacheControl{
"s-maxage": "1200",
}
respReqHdrs := http.Header{}
respReqTime := tenMinutesAgo
respRespTime := tenMinutesAgo
strictRFC := false
if reuse := CanReuseStored(reqHdr, respHdr, reqCC, respCC, respReqHdrs, respReqTime, respRespTime, strictRFC); reuse != remapdata.ReuseCan {
t.Errorf("CanReuseStored request before s-maxage but after Expires: expected ReuseCan, actual %v", reuse)
}
}
// test parent past s-maxage overrides future Expires and returns MustRevalidate
{
now := time.Now()
tenMinutesAgo := now.Add(time.Minute * -10)
fiveMinutesHence := now.Add(time.Minute * 5)
expires := fiveMinutesHence.Format(time.RFC1123)
reqHdr := http.Header{}
respHdr := http.Header{
"Date": {tenMinutesAgo.Format(time.RFC1123)},
"Expires": {expires},
"Cache-Control": {"s-maxage=300"},
}
reqCC := web.CacheControl{}
respCC := web.CacheControl{
"s-maxage": "300",
}
respReqHdrs := http.Header{}
respReqTime := tenMinutesAgo
respRespTime := tenMinutesAgo
strictRFC := false
if reuse := CanReuseStored(reqHdr, respHdr, reqCC, respCC, respReqHdrs, respReqTime, respRespTime, strictRFC); reuse != remapdata.ReuseMustRevalidateCanStale {
t.Errorf("CanReuseStored request after max-age but before Expires: expected ReuseMustRevalidateCanStale, actual %v", reuse)
}
}
// test parent future s-maxage overrides past Expires and past max-age and returns CanReuse
{
now := time.Now()
twentyMinutesAgo := now.Add(time.Minute * -10)
tenMinutesAgo := now.Add(time.Minute * -10)
expires := twentyMinutesAgo.Format(time.RFC1123)
reqHdr := http.Header{}
respHdr := http.Header{
"Date": {tenMinutesAgo.Format(time.RFC1123)},
"Expires": {expires},
"Cache-Control": {"s-maxage=1200,max-age=300"},
}
reqCC := web.CacheControl{}
respCC := web.CacheControl{
"s-maxage": "1200",
"max-age": "300",
}
respReqHdrs := http.Header{}
respReqTime := tenMinutesAgo
respRespTime := tenMinutesAgo
strictRFC := false
if reuse := CanReuseStored(reqHdr, respHdr, reqCC, respCC, respReqHdrs, respReqTime, respRespTime, strictRFC); reuse != remapdata.ReuseCan {
t.Errorf("CanReuseStored request before s-maxage but after Expires: expected ReuseCan, actual %v", reuse)
}
}
// test parent past s-maxage overrides future Expires and future max-age and returns MustRevalidate
{
now := time.Now()
tenMinutesAgo := now.Add(time.Minute * -10)
fiveMinutesHence := now.Add(time.Minute * 5)
expires := fiveMinutesHence.Format(time.RFC1123)
reqHdr := http.Header{}
respHdr := http.Header{
"Date": {tenMinutesAgo.Format(time.RFC1123)},
"Expires": {expires},
"Cache-Control": {"s-maxage=300,max-age=1200"},
}
reqCC := web.CacheControl{}
respCC := web.CacheControl{
"s-maxage": "300",
"max-age": "1200",
}
respReqHdrs := http.Header{}
respReqTime := tenMinutesAgo
respRespTime := tenMinutesAgo
strictRFC := false
if reuse := CanReuseStored(reqHdr, respHdr, reqCC, respCC, respReqHdrs, respReqTime, respRespTime, strictRFC); reuse != remapdata.ReuseMustRevalidateCanStale {
t.Errorf("CanReuseStored request after s-maxage but before Expires: expected ReuseMustRevalidateCanStale, actual %v", reuse)
}
}
// test client min-fresh is obeyed with strict RFC - tests RFC7234§5.2.1.3 compliance
{
now := time.Now()
tenMinutesAgo := now.Add(time.Minute * -10)
reqHdr := http.Header{
"Cache-Control": {"min-fresh=900"},
}
respHdr := http.Header{
"Date": {tenMinutesAgo.Format(time.RFC1123)},
"Cache-Control": {"max-age=1200"},
}
reqCC := web.CacheControl{
"min-fresh": "900",
}
respCC := web.CacheControl{
"max-age": "1200",
}
respReqHdrs := http.Header{}
respReqTime := tenMinutesAgo
respRespTime := tenMinutesAgo
strictRFC := true
if reuse := CanReuseStored(reqHdr, respHdr, reqCC, respCC, respReqHdrs, respReqTime, respRespTime, strictRFC); reuse != remapdata.ReuseMustRevalidate {
t.Errorf("CanReuseStored request with strictRFC min-fresh 300 with 600 remaining: expected ReuseMustRevalidate, actual %v", reuse)
}
}
// test client min-fresh is ignored without strict RFC - tests RFC7234§5.2.1.3 violation to protect origins
{
now := time.Now()
tenMinutesAgo := now.Add(time.Minute * -10)
reqHdr := http.Header{
"Cache-Control": {"min-fresh=900"},
}
respHdr := http.Header{
"Date": {tenMinutesAgo.Format(time.RFC1123)},
"Cache-Control": {"max-age=1200"},
}
reqCC := web.CacheControl{
"min-fresh": "900",
}
respCC := web.CacheControl{
"max-age": "1200",
}
respReqHdrs := http.Header{}
respReqTime := tenMinutesAgo
respRespTime := tenMinutesAgo
strictRFC := false
if reuse := CanReuseStored(reqHdr, respHdr, reqCC, respCC, respReqHdrs, respReqTime, respRespTime, strictRFC); reuse != remapdata.ReuseCan {
t.Errorf("CanReuseStored request with strictRFC min-fresh 1200 with 600 remaining: expected ReuseCan, actual %v", reuse)
}
}
// test client min-fresh is ignored without strict RFC - tests RFC7234§5.2.1.3 violation to protect origins
{
now := time.Now()
tenMinutesAgo := now.Add(time.Minute * -10)
reqHdr := http.Header{
"Cache-Control": {"min-fresh=900"},
}
respHdr := http.Header{
"Date": {tenMinutesAgo.Format(time.RFC1123)},
"Cache-Control": {"max-age=1200"},
}
reqCC := web.CacheControl{
"min-fresh": "900",
}
respCC := web.CacheControl{
"max-age": "1200",
}
respReqHdrs := http.Header{}
respReqTime := tenMinutesAgo
respRespTime := tenMinutesAgo
strictRFC := false
if reuse := CanReuseStored(reqHdr, respHdr, reqCC, respCC, respReqHdrs, respReqTime, respRespTime, strictRFC); reuse != remapdata.ReuseCan {
t.Errorf("CanReuseStored request with strictRFC min-fresh 1200 with 600 remaining: expected ReuseCan, actual %v", reuse)
}
}
// test default-cacheable response is cached. Tests RFC7234§5.2.1.3 and RFC7231§6.1 compliance
{
defaultCacheableCodes := []int{200, 203, 204, 206, 300, 301, 404, 405, 410, 414, 501} // RFC7231§6.1
for _, code := range defaultCacheableCodes {
reqHdr := http.Header{}
respCode := code
respHdr := http.Header{}
strictRFC := true
if !CanCache(http.MethodGet, reqHdr, respCode, respHdr, strictRFC) {
t.Errorf("CanCache returned false for request with no cache control and default-cacheable response code %v", code)
}
}
}
// test non-default-cacheable response with no Cache-Control is not cached. Tests RFC7234§5.2.1.3 compliance
{
nonDefaultCacheableCodes := []int{
201, 202, 205, 207, 208, 226,
302, 303, 304, 305, 306, 307, 308,
400, 401, 402, 403, 406, 407, 408, 409, 411, 412, 413, 4015, 416, 417, 418, 421, 422, 423, 424, 428, 429, 431, 451,
500, 502, 503, 504, 505, 506, 507, 508, 510, 511,
}
for _, code := range nonDefaultCacheableCodes {
reqHdr := http.Header{}
respCode := code
respHdr := http.Header{}
strictRFC := true
if CanCache(http.MethodGet, reqHdr, respCode, respHdr, strictRFC) {
t.Errorf("CanCache returned true for request with no cache control and non-default-cacheable response code %v", code)
}
}
}
// test non-default-cacheable response with Cache-Control is cached. Tests RFC7234§3 compliance
{
nonDefaultCacheableCodes := []int{
201, 202, 205, 207, 208, 226,
302, 303, 304, 305, 306, 307, 308,
400, 401, 402, 403, 406, 407, 408, 409, 411, 412, 413, 4015, 416, 417, 418, 421, 422, 423, 424, 428, 429, 431, 451,
500, 502, 503, 504, 505, 506, 507, 508, 510, 511,
}
cacheableRespHdrs := []map[string][]string{
{"Expires": {time.Now().Format(time.RFC1123)}},
{"Cache-Control": {"max-age=42"}},
{"Cache-Control": {"s-maxage=42"}},
}
for _, code := range nonDefaultCacheableCodes {
for _, hdr := range cacheableRespHdrs {
reqHdr := http.Header{}
respCode := code
respHdr := hdr
strictRFC := true
if !CanCache(http.MethodGet, reqHdr, respCode, respHdr, strictRFC) {
t.Errorf("CanCache returned false for request with non-default-cacheable response code %v and cacheable header %v", respCode, respHdr)
}
}
}
}
log.Init(log.NopCloser(os.Stdout), log.NopCloser(os.Stdout), log.NopCloser(os.Stdout), log.NopCloser(os.Stdout), log.NopCloser(os.Stdout))
}