Example #1
0
// IsResponseCacheable returs whether the upstream server allows the requested
// content to be saved in the cache. True result and 0 duration means that the
// response has no expiry date.
func IsResponseCacheable(code int, headers http.Header) bool {
	//!TODO: write a better custom implementation or fork the cacheobject - the API sucks
	//!TODO: correctly handle cache-control, pragma, etag and vary headers
	//!TODO: write unit tests

	if code != http.StatusOK && code != http.StatusPartialContent {
		return false
	}

	// For now, we do not cache encoded responses
	if headers.Get("Content-Encoding") != "" {
		return false
	}

	// We do not cache multipart range responses
	if strings.Contains(headers.Get("Content-Type"), "multipart/byteranges") {
		return false
	}

	respDir, err := cacheobject.ParseResponseCacheControl(headers.Get("Cache-Control"))
	if err != nil || respDir.NoCachePresent || respDir.NoStore || respDir.PrivatePresent {
		return false
	}

	return true
}
Example #2
0
// IsResponseCacheable returs whether the upstream server allows the requested
// content to be saved in the cache. True result and 0 duration means that the
// response has no expiry date.
func IsResponseCacheable(resp *http.Response) (bool, time.Duration) {
	//!TODO: write a better custom implementation or fork the cacheobject - the API sucks
	//!TODO: correctly handle cache-control, pragma, etag and vary headers
	//!TODO: write unit tests

	respDir, _ := cacheobject.ParseResponseCacheControl(resp.Header.Get("Cache-Control"))
	return !(respDir.NoCachePresent || respDir.NoStore || respDir.PrivatePresent), 0
}
Example #3
0
func main() {
	req, _ := http.NewRequest("GET", "http://www.example.com/", nil)

	res, _ := http.DefaultClient.Do(req)
	_, _ = ioutil.ReadAll(res.Body)

	reqDir, _ := cacheobject.ParseRequestCacheControl(req.Header.Get("Cache-Control"))

	resDir, _ := cacheobject.ParseResponseCacheControl(res.Header.Get("Cache-Control"))
	expiresHeader, _ := http.ParseTime(res.Header.Get("Expires"))
	dateHeader, _ := http.ParseTime(res.Header.Get("Date"))
	lastModifiedHeader, _ := http.ParseTime(res.Header.Get("Last-Modified"))

	obj := cacheobject.Object{
		RespDirectives:         resDir,
		RespHeaders:            res.Header,
		RespStatusCode:         res.StatusCode,
		RespExpiresHeader:      expiresHeader,
		RespDateHeader:         dateHeader,
		RespLastModifiedHeader: lastModifiedHeader,

		ReqDirectives: reqDir,
		ReqHeaders:    req.Header,
		ReqMethod:     req.Method,

		NowUTC: time.Now().UTC(),
	}
	rv := cacheobject.ObjectResults{}

	cacheobject.CachableObject(&obj, &rv)
	cacheobject.ExpirationObject(&obj, &rv)

	fmt.Println("Errors: ", rv.OutErr)
	fmt.Println("Reasons to not cache: ", rv.OutReasons)
	fmt.Println("Warning headers to add: ", rv.OutWarnings)
	fmt.Println("Expiration: ", rv.OutExpirationTime.String())
}
Example #4
0
// ResponseExpiresIn parses the expiration time from upstream headers, if any, and returns
// it as a duration from now. If no expire time is found, it returns its second argument:
// the default expiration time.
func ResponseExpiresIn(headers http.Header, ifNotAny time.Duration) time.Duration {

	//!TODO: this cacheobject.ParseResponseCacheControl is called two times for every
	// caceable response. We should find a way not to duplicate work. Maybe pass the resout
	// around somehow?
	respDir, err := cacheobject.ParseResponseCacheControl(headers.Get("Cache-Control"))
	if err != nil {
		return ifNotAny
	}

	if respDir.SMaxAge > 0 {
		return time.Duration(respDir.SMaxAge) * time.Second
	} else if respDir.MaxAge > 0 {
		return time.Duration(respDir.MaxAge) * time.Second
	} else if headers.Get("Expires") != "" {
		_ = "breakpoint"
		if t, err := time.Parse(time.RFC1123, headers.Get("Expires")); err == nil {
			//!TODO: use the server time from the Date header to calculate?
			return t.Sub(time.Now())
		}
	}

	return ifNotAny
}