You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@trafficcontrol.apache.org by ro...@apache.org on 2018/05/23 16:34:09 UTC
[incubator-trafficcontrol] 03/04: Add rfc package,
move stuff around to accomodate
This is an automated email from the ASF dual-hosted git repository.
rob pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/incubator-trafficcontrol.git
commit 4449a94fa84609a6d0949f3ffb6652686cd121c3
Author: Jan van Doorn <ja...@comcast.com>
AuthorDate: Wed May 16 08:58:06 2018 -0600
Add rfc package, move stuff around to accomodate
---
grove/cache/handler.go | 3 +-
grove/cache/retryinggetter.go | 5 +-
grove/integration_test/compare_gets.go | 140 +++++++++++++++++++++++++++
grove/plugin/http_cacheinspector.go | 3 +-
grove/remap/remap.go | 7 +-
grove/{remap => rfc}/rules.go | 166 +++++++++++++++++++++++++++++++--
grove/{remap => rfc}/rules_test.go | 6 +-
grove/web/util.go | 145 +---------------------------
8 files changed, 314 insertions(+), 161 deletions(-)
diff --git a/grove/cache/handler.go b/grove/cache/handler.go
index a7a7f4a..216378c 100644
--- a/grove/cache/handler.go
+++ b/grove/cache/handler.go
@@ -27,6 +27,7 @@ import (
"github.com/apache/incubator-trafficcontrol/grove/remap"
"github.com/apache/incubator-trafficcontrol/grove/remapdata"
+ "github.com/apache/incubator-trafficcontrol/grove/rfc"
"github.com/apache/incubator-trafficcontrol/grove/stat"
"github.com/apache/incubator-trafficcontrol/grove/thread"
"github.com/apache/incubator-trafficcontrol/grove/web"
@@ -261,7 +262,7 @@ func (h *Handler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
}
reqHeaders := r.Header
- canReuseStored := remap.CanReuseStored(reqHeaders, cacheObj.RespHeaders, reqCacheControl, cacheObj.RespCacheControl, cacheObj.ReqHeaders, cacheObj.ReqRespTime, cacheObj.RespRespTime, h.strictRFC)
+ canReuseStored := rfc.CanReuseStored(reqHeaders, cacheObj.RespHeaders, reqCacheControl, cacheObj.RespCacheControl, cacheObj.ReqHeaders, cacheObj.ReqRespTime, cacheObj.RespRespTime, h.strictRFC)
if canReuseStored != remapdata.ReuseCan { // run the BeforeParentRequest hook for revalidations / ReuseCannot
beforeParentRequestData := plugin.BeforeParentRequestData{Req: r, RemapRule: remappingProducer.Name()}
diff --git a/grove/cache/retryinggetter.go b/grove/cache/retryinggetter.go
index ee60017..b70a6d4 100644
--- a/grove/cache/retryinggetter.go
+++ b/grove/cache/retryinggetter.go
@@ -23,6 +23,7 @@ import (
"github.com/apache/incubator-trafficcontrol/grove/cacheobj"
"github.com/apache/incubator-trafficcontrol/grove/icache"
"github.com/apache/incubator-trafficcontrol/grove/remap"
+ "github.com/apache/incubator-trafficcontrol/grove/rfc"
"github.com/apache/incubator-trafficcontrol/grove/thread"
"github.com/apache/incubator-trafficcontrol/grove/web"
@@ -56,7 +57,7 @@ func (r *Retrier) Get(req *http.Request, obj *cacheobj.CacheObj) (*cacheobj.Cach
retryGetFunc := func(remapping remap.Remapping, retryFailures bool, obj *cacheobj.CacheObj) *cacheobj.CacheObj {
// return true for Revalidate, and issue revalidate requests separately.
canReuse := func(cacheObj *cacheobj.CacheObj) bool {
- return remap.CanReuse(r.ReqHdr, r.ReqCacheControl, cacheObj, r.H.strictRFC, true)
+ return rfc.CanReuse(r.ReqHdr, r.ReqCacheControl, cacheObj, r.H.strictRFC, true)
}
getAndCache := func() *cacheobj.CacheObj {
return GetAndCache(remapping.Request, remapping.ProxyURL, remapping.CacheKey, remapping.Name, remapping.Request.Header, r.ReqTime, r.H.strictRFC, remapping.Cache, r.H.ruleThrottlers[remapping.Name], obj, remapping.Timeout, retryFailures, remapping.RetryNum, remapping.RetryCodes, remapping.Transport, r.ReqID)
@@ -166,7 +167,7 @@ func GetAndCache(
if revalidateObj == nil || respCode != http.StatusNotModified {
log.Debugf("GetAndCache new %v (reqid %v)\n", cacheKey, reqID)
obj = cacheobj.New(reqHeader, respBody, respCode, respCode, proxyURLStr, respHeader, reqTime, reqRespTime, respRespTime, lastModified)
- if !remap.CanCache(req.Method, reqHeader, respCode, respHeader, strictRFC) {
+ if !rfc.CanCache(req.Method, reqHeader, respCode, respHeader, strictRFC) {
return obj // return without caching
}
} else {
diff --git a/grove/integration_test/compare_gets.go b/grove/integration_test/compare_gets.go
new file mode 100644
index 0000000..08d3aca
--- /dev/null
+++ b/grove/integration_test/compare_gets.go
@@ -0,0 +1,140 @@
+package main
+
+import (
+ "flag"
+ "fmt"
+ "github.com/apache/incubator-trafficcontrol/grove/web"
+ "io/ioutil"
+ "log"
+ "net/http"
+ "os"
+ "strings"
+)
+
+type responseType struct {
+ Headers http.Header
+ Body []byte
+}
+
+func httpGet(URL, headers string) responseType {
+ client := &http.Client{}
+ req, err := http.NewRequest("GET", URL, nil)
+ if err != nil {
+ fmt.Println("ERROR in httpGet")
+ }
+ //log.Printf(">>>%v<<< %v\n", headers, len(strings.Split(headers, ".")))
+ for _, hdrString := range strings.Split(headers, ",") {
+ //log.Println(">>> ", hdrString)
+ if hdrString == "" {
+ continue
+ }
+ parts := strings.Split(hdrString, ":")
+ if parts[0] == "Host" {
+ req.Host = parts[1]
+ } else {
+ //log.Println("> ", parts)
+ req.Header.Set(parts[0], parts[1])
+ }
+ }
+ //log.Printf(">>>> %v", req)
+ resp, err := client.Do(req)
+ if err != nil {
+ fmt.Println("ERROR in httpGet")
+ }
+ defer resp.Body.Close()
+ var response responseType
+ response.Headers = web.CopyHeader(resp.Header)
+ response.Body, err = ioutil.ReadAll(resp.Body)
+ if err != nil {
+ fmt.Println("ERROR in httpGet (readall)")
+ }
+ return response
+}
+
+func equal(a, b []byte) bool {
+ if a == nil || b == nil {
+ return false
+ }
+
+ if a == nil && b == nil {
+ return true
+ }
+ if len(a) != len(b) {
+ return false
+ }
+
+ for i := range a {
+ if a[i] != b[i] {
+ return false
+ }
+ }
+
+ return true
+}
+
+func equalStringSlices(a, b []string) bool {
+ if a == nil || b == nil {
+ return false
+ }
+
+ if a == nil && b == nil {
+ return true
+ }
+ if len(a) != len(b) {
+ return false
+ }
+
+ for i := range a {
+ if a[i] != b[i] {
+ return false
+ }
+ }
+
+ return true
+}
+
+func inStringSlice(str string, arr []string) bool {
+ for _, strEnt := range arr {
+ if strEnt == str {
+ return true
+ }
+ }
+ return false
+}
+
+func compareResponses(response1 responseType, response2 responseType, ignoreHdrs []string) bool {
+ if !equal(response1.Body, response2.Body) {
+ return false
+ }
+ for hdrKey, _ := range response1.Headers {
+ if inStringSlice(hdrKey, ignoreHdrs) {
+ continue
+ }
+ if !equalStringSlices(response1.Headers[hdrKey], response2.Headers[hdrKey]) {
+ log.Printf("ERROR hdr %v doesn't match: \"%v\" != \"%v\"\n", hdrKey, response1.Headers[hdrKey], response2.Headers[hdrKey])
+ return false
+ }
+ //fmt.Printf(">>>>> %v\n", hdrKey)
+ }
+
+ return true
+}
+func main() {
+ originURL := flag.String("org", "http://localhost", "The origin URL (default: \"http://localhost\")")
+ cacheURL := flag.String("cache", "http://localhost:8080", "The cache URL (default: \"http://localhost:8080\")")
+ path := flag.String("path", "", "The path to GET")
+ orgHdrs := flag.String("ohdrs", "", "Comma seperated list of headers to add to origin request")
+ cacheHdrs := flag.String("chdrs", "", "Comma separated list of headers to add to cache request")
+ ignoreHdrs := flag.String("ignorehdrs", "Server,Date", "Comma separated list of headers to ignore in the compare")
+ flag.Parse()
+
+ resp := httpGet(*originURL+"/"+*path, *orgHdrs)
+ cresp := httpGet(*cacheURL+"/"+*path, *cacheHdrs)
+ if !compareResponses(resp, cresp, strings.Split(*ignoreHdrs, ",")) {
+ fmt.Println("FAIL: Body bytes don't match")
+ os.Exit(1)
+
+ }
+ fmt.Println("PASS")
+ os.Exit(0)
+}
diff --git a/grove/plugin/http_cacheinspector.go b/grove/plugin/http_cacheinspector.go
index 2cdb158..9409aee 100644
--- a/grove/plugin/http_cacheinspector.go
+++ b/grove/plugin/http_cacheinspector.go
@@ -26,6 +26,7 @@ import (
"github.com/apache/incubator-trafficcontrol/grove/web"
"time"
+ "github.com/apache/incubator-trafficcontrol/grove/rfc"
"github.com/apache/incubator-trafficcontrol/lib/go-log"
)
@@ -178,7 +179,7 @@ func cacheinspect(icfg interface{}, d OnRequestData) bool {
cacheObject, _ := d.Stats.CachePeek(key, cName)
age := time.Now().Sub(cacheObject.ReqRespTime)
- freshFor := web.FreshFor(cacheObject.RespHeaders, cacheObject.RespCacheControl, cacheObject.ReqRespTime, cacheObject.RespRespTime)
+ freshFor := rfc.FreshFor(cacheObject.RespHeaders, cacheObject.RespCacheControl, cacheObject.ReqRespTime, cacheObject.RespRespTime)
w.Write([]byte(fmt.Sprintf(" %8d%8d%10s%22v%22v%12d <a href=\"http://%s%s?key=%s&cache=%s\">%s</a>\n",
i, cacheObject.Code, bytefmt.ByteSize(cacheObject.Size), age, freshFor, cacheObject.HitCount, req.Host, CacheStatsEndpoint, url.QueryEscape(key), cName, key)))
}
diff --git a/grove/remap/remap.go b/grove/remap/remap.go
index 2af529c..9793566 100644
--- a/grove/remap/remap.go
+++ b/grove/remap/remap.go
@@ -29,6 +29,7 @@ import (
"github.com/apache/incubator-trafficcontrol/grove/icache"
"github.com/apache/incubator-trafficcontrol/grove/plugin"
"github.com/apache/incubator-trafficcontrol/grove/remapdata"
+ "github.com/apache/incubator-trafficcontrol/grove/rfc"
"github.com/apache/incubator-trafficcontrol/grove/web"
"github.com/apache/incubator-trafficcontrol/lib/go-log"
@@ -336,7 +337,7 @@ func LoadRemapRules(path string, pluginConfigLoaders map[string]plugin.LoadFunc,
if remapRulesJSON.RetryCodes != nil {
remapRules.RetryCodes = make(map[int]struct{}, len(*remapRulesJSON.RetryCodes))
for _, code := range *remapRulesJSON.RetryCodes {
- if _, ok := ValidHTTPCodes[code]; !ok {
+ if _, ok := rfc.ValidHTTPCodes[code]; !ok {
return nil, nil, nil, fmt.Errorf("error parsing rules: retry code invalid: %v", code)
}
remapRules.RetryCodes[code] = struct{}{}
@@ -392,7 +393,7 @@ func LoadRemapRules(path string, pluginConfigLoaders map[string]plugin.LoadFunc,
if jsonRule.RetryCodes != nil {
rule.RetryCodes = make(map[int]struct{}, len(*jsonRule.RetryCodes))
for _, code := range *jsonRule.RetryCodes {
- if _, ok := ValidHTTPCodes[code]; !ok {
+ if _, ok := rfc.ValidHTTPCodes[code]; !ok {
return nil, nil, nil, fmt.Errorf("error parsing rule %v retry code invalid: %v", rule.Name, code)
}
rule.RetryCodes[code] = struct{}{}
@@ -511,7 +512,7 @@ func makeTo(tosJSON []RemapRuleToJSON, rule remapdata.RemapRule, baseTransport *
if toJSON.RetryCodes != nil {
to.RetryCodes = make(map[int]struct{}, len(*toJSON.RetryCodes))
for _, code := range *toJSON.RetryCodes {
- if _, ok := ValidHTTPCodes[code]; !ok {
+ if _, ok := rfc.ValidHTTPCodes[code]; !ok {
return nil, fmt.Errorf("error parsing to %v retry code invalid: %v", to.URL, code)
}
to.RetryCodes[code] = struct{}{}
diff --git a/grove/remap/rules.go b/grove/rfc/rules.go
similarity index 68%
rename from grove/remap/rules.go
rename to grove/rfc/rules.go
index 31ba715..1879bc7 100644
--- a/grove/remap/rules.go
+++ b/grove/rfc/rules.go
@@ -1,4 +1,4 @@
-package remap
+package rfc
/*
Licensed under the Apache License, Version 2.0 (the "License");
@@ -14,6 +14,10 @@ package remap
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"
"strings"
@@ -24,6 +28,8 @@ import (
"github.com/apache/incubator-trafficcontrol/grove/web"
"github.com/apache/incubator-trafficcontrol/lib/go-log"
+ "math"
+ "strconv"
)
// ValidHTTPCodes provides fast lookup whether a HTTP response code is valid per RFC7234§3
@@ -266,8 +272,8 @@ func fresh(
respReqTime time.Time,
respRespTime time.Time,
) bool {
- freshnessLifetime := web.GetFreshnessLifetime(respHeaders, respCacheControl)
- currentAge := web.GetCurrentAge(respHeaders, respReqTime, respRespTime)
+ freshnessLifetime := getFreshnessLifetime(respHeaders, respCacheControl)
+ currentAge := getCurrentAge(respHeaders, respReqTime, respRespTime)
log.Debugf("Fresh: freshnesslifetime %v currentAge %v\n", freshnessLifetime, currentAge)
fresh := freshnessLifetime > currentAge
return fresh
@@ -275,12 +281,12 @@ func fresh(
// inMinFresh returns whether the given response is within the `min-fresh` request directive. If no `min-fresh` directive exists in the request, `true` is returned.
func inMinFresh(respHeaders http.Header, reqCacheControl web.CacheControl, respCacheControl web.CacheControl, respReqTime time.Time, respRespTime time.Time) bool {
- minFresh, ok := web.GetHTTPDeltaSecondsCacheControl(reqCacheControl, "min-fresh")
+ minFresh, ok := getHTTPDeltaSecondsCacheControl(reqCacheControl, "min-fresh")
if !ok {
return true // no min-fresh => within min-fresh
}
- freshnessLifetime := web.GetFreshnessLifetime(respHeaders, respCacheControl)
- currentAge := web.GetCurrentAge(respHeaders, respReqTime, respRespTime)
+ freshnessLifetime := getFreshnessLifetime(respHeaders, respCacheControl)
+ currentAge := getCurrentAge(respHeaders, respReqTime, respRespTime)
inMinFresh := minFresh < (freshnessLifetime - currentAge)
log.Debugf("inMinFresh minFresh %v freshnessLifetime %v currentAge %v => %v < (%v - %v) = %v\n", minFresh, freshnessLifetime, currentAge, minFresh, freshnessLifetime, currentAge, inMinFresh)
return inMinFresh
@@ -322,13 +328,13 @@ func allowedStale(respHeaders http.Header, reqCacheControl web.CacheControl, res
// InMaxStale returns whether the given response is within the `max-stale` request directive. If no `max-stale` directive exists in the request, `true` is returned.
func inMaxStale(respHeaders http.Header, respCacheControl web.CacheControl, respReqTime time.Time, respRespTime time.Time) bool {
- maxStale, ok := web.GetHTTPDeltaSecondsCacheControl(respCacheControl, "max-stale")
+ maxStale, ok := getHTTPDeltaSecondsCacheControl(respCacheControl, "max-stale")
if !ok {
// maxStale = 5 // debug
return true // no max-stale => within max-stale
}
- freshnessLifetime := web.GetFreshnessLifetime(respHeaders, respCacheControl)
- currentAge := web.GetCurrentAge(respHeaders, respReqTime, respRespTime)
+ freshnessLifetime := getFreshnessLifetime(respHeaders, respCacheControl)
+ currentAge := getCurrentAge(respHeaders, respReqTime, respRespTime)
log.Errorf("DEBUGR InMaxStale maxStale %v freshnessLifetime %v currentAge %v => %v > (%v, %v)\n", maxStale, freshnessLifetime, currentAge, maxStale, currentAge, freshnessLifetime) // DEBUG
inMaxStale := maxStale > (currentAge - freshnessLifetime)
return inMaxStale
@@ -378,3 +384,145 @@ func hasPragmaNoCache(reqHeaders http.Header) bool {
}
return false
}
+
+// GetHTTPDeltaSeconds is a helper function which gets an HTTP Delta Seconds from the given map (which is typically a `http.Header` or `CacheControl`. Returns false if the given key doesn't exist in the map, or if the value isn't a valid Delta Seconds per RFC2616§3.3.2.
+func getHTTPDeltaSeconds(m map[string][]string, key string) (time.Duration, bool) {
+ maybeSeconds, ok := m[key]
+ if !ok {
+ return 0, false
+ }
+ if len(maybeSeconds) == 0 {
+ return 0, false
+ }
+ maybeSec := maybeSeconds[0]
+
+ seconds, err := strconv.ParseUint(maybeSec, 10, 64)
+ if err != nil {
+ return 0, false
+ }
+ return time.Duration(seconds) * time.Second, true
+}
+
+// getHTTPDeltaSeconds is a helper function which gets an HTTP Delta Seconds from the given map (which is typically a `http.Header` or `CacheControl`. Returns false if the given key doesn't exist in the map, or if the value isn't a valid Delta Seconds per RFC2616§3.3.2.
+func getHTTPDeltaSecondsCacheControl(m map[string]string, key string) (time.Duration, bool) {
+ maybeSec, ok := m[key]
+ if !ok {
+ return 0, false
+ }
+ seconds, err := strconv.ParseUint(maybeSec, 10, 64)
+ if err != nil {
+ return 0, false
+ }
+ return time.Duration(seconds) * time.Second, true
+}
+
+// getFreshnessLifetime calculates the freshness_lifetime per RFC7234§4.2.1
+func getFreshnessLifetime(respHeaders http.Header, respCacheControl web.CacheControl) time.Duration {
+ if s, ok := getHTTPDeltaSecondsCacheControl(respCacheControl, "s-maxage"); ok {
+ return s
+ }
+ if s, ok := getHTTPDeltaSecondsCacheControl(respCacheControl, "max-age"); ok {
+ return s
+ }
+
+ getExpires := func() (time.Duration, bool) {
+ expires, ok := web.GetHTTPDate(respHeaders, "Expires")
+ if !ok {
+ return 0, false
+ }
+ date, ok := web.GetHTTPDate(respHeaders, "Date")
+ if !ok {
+ return 0, false
+ }
+ return expires.Sub(date), true
+ }
+ if s, ok := getExpires(); ok {
+ return s
+ }
+ return heuristicFreshness(respHeaders)
+}
+
+const Day = time.Hour * time.Duration(24)
+
+// HeuristicFreshness follows the recommendation of RFC7234§4.2.2 and returns the min of 10% of the (Date - Last-Modified) headers and 24 hours, if they exist, and 24 hours if they don't.
+// TODO: smarter and configurable heuristics
+func heuristicFreshness(respHeaders http.Header) time.Duration {
+ sinceLastModified, ok := sinceLastModified(respHeaders)
+ if !ok {
+ return Day
+ }
+ freshness := time.Duration(math.Min(float64(Day), float64(sinceLastModified)))
+ return freshness
+}
+
+func sinceLastModified(headers http.Header) (time.Duration, bool) {
+ lastModified, ok := web.GetHTTPDate(headers, "last-modified")
+ if !ok {
+ return 0, false
+ }
+ date, ok := web.GetHTTPDate(headers, "date")
+ if !ok {
+ return 0, false
+ }
+ return date.Sub(lastModified), true
+}
+
+// ageValue is used to calculate current_age per RFC7234§4.2.3
+func ageValue(respHeaders http.Header) time.Duration {
+ s, ok := getHTTPDeltaSeconds(respHeaders, "age")
+ if !ok {
+ return 0
+ }
+ return s
+}
+
+// dateValue is used to calculate current_age per RFC7234§4.2.3. It returns time, or false if the response had no Date header (in violation of HTTP/1.1).
+func dateValue(respHeaders http.Header) (time.Time, bool) {
+ return web.GetHTTPDate(respHeaders, "date")
+}
+
+func apparentAge(respHeaders http.Header, respRespTime time.Time) time.Duration {
+ dateValue, ok := dateValue(respHeaders)
+ if !ok {
+ return 0 // TODO log warning?
+ }
+ rawAge := respRespTime.Sub(dateValue)
+ return time.Duration(math.Max(0.0, float64(rawAge)))
+}
+
+func responseDelay(respReqTime time.Time, respRespTime time.Time) time.Duration {
+ return respRespTime.Sub(respReqTime)
+}
+
+func correctedAgeValue(respHeaders http.Header, respReqTime time.Time, respRespTime time.Time) time.Duration {
+ return ageValue(respHeaders) + responseDelay(respReqTime, respRespTime)
+}
+
+func correctedInitialAge(respHeaders http.Header, respReqTime time.Time, respRespTime time.Time) time.Duration {
+ return time.Duration(math.Max(float64(apparentAge(respHeaders, respRespTime)), float64(correctedAgeValue(respHeaders, respReqTime, respRespTime))))
+}
+
+func residentTime(respRespTime time.Time) time.Duration {
+ return time.Now().Sub(respRespTime)
+}
+
+func getCurrentAge(respHeaders http.Header, respReqTime time.Time, respRespTime time.Time) time.Duration {
+ correctedInitial := correctedInitialAge(respHeaders, respReqTime, respRespTime)
+ resident := residentTime(respRespTime)
+ log.Debugf("getCurrentAge: correctedInitialAge %v residentTime %v\n", correctedInitial, resident)
+ return correctedInitial + resident
+}
+
+// FreshFor checks returns how long this object is still good for
+func FreshFor(
+ respHeaders http.Header,
+ respCacheControl web.CacheControl,
+ respReqTime time.Time,
+ respRespTime time.Time,
+) time.Duration {
+ freshnessLifetime := getFreshnessLifetime(respHeaders, respCacheControl)
+ currentAge := getCurrentAge(respHeaders, respReqTime, respRespTime)
+ log.Debugf("FreshFor: freshnesslifetime %v currentAge %v\n", freshnessLifetime, currentAge)
+ //fresh := freshnessLifetime > currentAge
+ return freshnessLifetime - currentAge
+}
diff --git a/grove/remap/rules_test.go b/grove/rfc/rules_test.go
similarity index 99%
rename from grove/remap/rules_test.go
rename to grove/rfc/rules_test.go
index 91bdde9..f364aad 100644
--- a/grove/remap/rules_test.go
+++ b/grove/rfc/rules_test.go
@@ -1,4 +1,4 @@
-package remap
+package rfc
/*
Licensed under the Apache License, Version 2.0 (the "License");
@@ -14,6 +14,10 @@ package remap
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"
diff --git a/grove/web/util.go b/grove/web/util.go
index 23c7922..ff13e44 100644
--- a/grove/web/util.go
+++ b/grove/web/util.go
@@ -22,9 +22,8 @@ import (
"strings"
"time"
+ //"github.com/apache/incubator-trafficcontrol/grove/rfc"
"github.com/apache/incubator-trafficcontrol/lib/go-log"
- "math"
- "strconv"
)
type Hdr struct {
@@ -200,145 +199,3 @@ func ParseHTTPDate(d string) (time.Time, bool) {
// RemapTextKey is the plugin shared data key inserted by grovetccfg for the Remap Line of the Delivery Service in Traffic Control, Traffic Ops.
const RemapTextKey = "remap_text"
-
-const Day = time.Hour * time.Duration(24)
-
-// GetHTTPDeltaSeconds is a helper function which gets an HTTP Delta Seconds from the given map (which is typically a `http.Header` or `CacheControl`. Returns false if the given key doesn't exist in the map, or if the value isn't a valid Delta Seconds per RFC2616§3.3.2.
-func GetHTTPDeltaSeconds(m map[string][]string, key string) (time.Duration, bool) {
- maybeSeconds, ok := m[key]
- if !ok {
- return 0, false
- }
- if len(maybeSeconds) == 0 {
- return 0, false
- }
- maybeSec := maybeSeconds[0]
-
- seconds, err := strconv.ParseUint(maybeSec, 10, 64)
- if err != nil {
- return 0, false
- }
- return time.Duration(seconds) * time.Second, true
-}
-
-// GetHTTPDeltaSeconds is a helper function which gets an HTTP Delta Seconds from the given map (which is typically a `http.Header` or `CacheControl`. Returns false if the given key doesn't exist in the map, or if the value isn't a valid Delta Seconds per RFC2616§3.3.2.
-func GetHTTPDeltaSecondsCacheControl(m map[string]string, key string) (time.Duration, bool) {
- maybeSec, ok := m[key]
- if !ok {
- return 0, false
- }
- seconds, err := strconv.ParseUint(maybeSec, 10, 64)
- if err != nil {
- return 0, false
- }
- return time.Duration(seconds) * time.Second, true
-}
-
-// HeuristicFreshness follows the recommendation of RFC7234§4.2.2 and returns the min of 10% of the (Date - Last-Modified) headers and 24 hours, if they exist, and 24 hours if they don't.
-// TODO: smarter and configurable heuristics
-func HeuristicFreshness(respHeaders http.Header) time.Duration {
- sinceLastModified, ok := sinceLastModified(respHeaders)
- if !ok {
- return Day
- }
- freshness := time.Duration(math.Min(float64(Day), float64(sinceLastModified)))
- return freshness
-}
-
-func sinceLastModified(headers http.Header) (time.Duration, bool) {
- lastModified, ok := GetHTTPDate(headers, "last-modified")
- if !ok {
- return 0, false
- }
- date, ok := GetHTTPDate(headers, "date")
- if !ok {
- return 0, false
- }
- return date.Sub(lastModified), true
-}
-
-// GetFreshnessLifetime calculates the freshness_lifetime per RFC7234§4.2.1
-func GetFreshnessLifetime(respHeaders http.Header, respCacheControl CacheControl) time.Duration {
- if s, ok := GetHTTPDeltaSecondsCacheControl(respCacheControl, "s-maxage"); ok {
- return s
- }
- if s, ok := GetHTTPDeltaSecondsCacheControl(respCacheControl, "max-age"); ok {
- return s
- }
-
- getExpires := func() (time.Duration, bool) {
- expires, ok := GetHTTPDate(respHeaders, "Expires")
- if !ok {
- return 0, false
- }
- date, ok := GetHTTPDate(respHeaders, "Date")
- if !ok {
- return 0, false
- }
- return expires.Sub(date), true
- }
- if s, ok := getExpires(); ok {
- return s
- }
- return HeuristicFreshness(respHeaders)
-}
-
-// t6AgeValue is used to calculate current_age per RFC7234§4.2.3
-func AgeValue(respHeaders http.Header) time.Duration {
- s, ok := GetHTTPDeltaSeconds(respHeaders, "age")
- if !ok {
- return 0
- }
- return s
-}
-
-func GetCurrentAge(respHeaders http.Header, respReqTime time.Time, respRespTime time.Time) time.Duration {
- correctedInitial := CorrectedInitialAge(respHeaders, respReqTime, respRespTime)
- resident := residentTime(respRespTime)
- log.Debugf("getCurrentAge: correctedInitialAge %v residentTime %v\n", correctedInitial, resident)
- return correctedInitial + resident
-}
-
-func CorrectedInitialAge(respHeaders http.Header, respReqTime time.Time, respRespTime time.Time) time.Duration {
- return time.Duration(math.Max(float64(ApparentAge(respHeaders, respRespTime)), float64(CorrectedAgeValue(respHeaders, respReqTime, respRespTime))))
-}
-
-func CorrectedAgeValue(respHeaders http.Header, respReqTime time.Time, respRespTime time.Time) time.Duration {
- return AgeValue(respHeaders) + responseDelay(respReqTime, respRespTime)
-}
-
-func responseDelay(respReqTime time.Time, respRespTime time.Time) time.Duration {
- return respRespTime.Sub(respReqTime)
-}
-
-func residentTime(respRespTime time.Time) time.Duration {
- return time.Now().Sub(respRespTime)
-}
-
-func ApparentAge(respHeaders http.Header, respRespTime time.Time) time.Duration {
- dateValue, ok := dateValue(respHeaders)
- if !ok {
- return 0 // TODO log warning?
- }
- rawAge := respRespTime.Sub(dateValue)
- return time.Duration(math.Max(0.0, float64(rawAge)))
-}
-
-// dateValue is used to calculate current_age per RFC7234§4.2.3. It returns time, or false if the response had no Date header (in violation of HTTP/1.1).
-func dateValue(respHeaders http.Header) (time.Time, bool) {
- return GetHTTPDate(respHeaders, "date")
-}
-
-// FreshFor checks returns how long this object is still good for
-func FreshFor(
- respHeaders http.Header,
- respCacheControl CacheControl,
- respReqTime time.Time,
- respRespTime time.Time,
-) time.Duration {
- freshnessLifetime := GetFreshnessLifetime(respHeaders, respCacheControl)
- currentAge := GetCurrentAge(respHeaders, respReqTime, respRespTime)
- log.Debugf("FreshFor: freshnesslifetime %v currentAge %v\n", freshnessLifetime, currentAge)
- //fresh := freshnessLifetime > currentAge
- return freshnessLifetime - currentAge
-}
--
To stop receiving notification emails like this one, please contact
rob@apache.org.