func NewLeveldbCacheTransport() *Transport { tempDir, err := ioutil.TempDir("", "httpleveldbcache") if err != nil { glog.Errorf("TempDir err = %v", err) return nil } glog.Infof("NewLeveldbCacheTransport tempDir = %v", tempDir) c, err := leveldbcache.New(fmt.Sprintf("%s%c%s", tempDir, os.PathSeparator, "db")) if err != nil { glog.Errorf("New err = %v", err) return nil } t := NewTransport(c) return t }
func NewDiskCacheTransport() *Transport { tempDir, err := ioutil.TempDir("", "httpdiskcache") if err != nil { glog.Errorf("TempDir err = %v", err) return nil } glog.Infof("NewDiskCacheTransport tempDir = %v", tempDir) c := diskcache.New(tempDir) t := NewTransport(c) return t }
// RoundTrip takes a Request and returns a Response // // If there is a fresh Response already in cache, then it will be returned without connecting to // the server. // // If there is a stale Response, then any validators it contains will be set on the new request // to give the server a chance to respond with NotModified. If this happens, then the cached Response // will be returned. func (t *Transport) RoundTrip(req *http.Request) (resp *http.Response, err error) { cacheKey := cacheKey(req) cacheableMethod := req.Method == "GET" || req.Method == "HEAD" var cachedResp *http.Response if cacheableMethod { cachedResp, err = CachedResponse(t.Cache, req) } else { // Need to invalidate an existing value t.Cache.Delete(cacheKey) } transport := t.Transport if transport == nil { //john change 2015.11.3 if true { var ok bool if transport, ok = http.DefaultTransport.(*http.Transport); !ok { return nil, err } } else { //transport = http.DefaultTransport } } if cachedResp != nil && err == nil && cacheableMethod && req.Header.Get("range") == "" { if t.MarkCachedResponses { cachedResp.Header.Set(XFromCache, "1") } if varyMatches(cachedResp, req) { // Can only use cached value if the new request doesn't Vary significantly freshness := getFreshness(cachedResp.Header, req.Header) if freshness == fresh { glog.V(logVNum).Infof("fresh url = %v", req.URL.String()) return cachedResp, nil } if freshness == stale { var req2 *http.Request // Add validators if caller hasn't already done so //glog.V(logVNum).Infof("stale url = %v", req.URL.String()) etag := cachedResp.Header.Get("etag") if etag != "" { reqEtag := req.Header.Get("etag") if etag == reqEtag { return RespNotModified(cachedResp, req) } reqIfNoneMatch := req.Header.Get("if-none-match") if etag == reqIfNoneMatch { return RespNotModified(cachedResp, req) } if reqEtag == "" && reqIfNoneMatch == "" { req2 = cloneRequest(req) req2.Header.Set("if-none-match", etag) glog.V(logVNum).Infof("Header.Set if-none-match : %v", etag) } } lastModified := cachedResp.Header.Get("last-modified") if lastModified != "" { reqLastModified := req.Header.Get("last-modified") if result, err := headerTimeCmp(lastModified, reqLastModified); err == nil { if result >= 0 { return RespNotModified(cachedResp, req) } } reqIfModifiedSince := req.Header.Get("if-modified-since") if result, err := headerTimeCmp(lastModified, reqIfModifiedSince); err == nil { if result >= 0 { return RespNotModified(cachedResp, req) } } if reqLastModified == "" && reqIfModifiedSince == "" { if req2 == nil { req2 = cloneRequest(req) } req2.Header.Set("if-modified-since", lastModified) glog.V(logVNum).Infof("Header.Set if-modified-since : %v", lastModified) } } if req2 != nil { // Associate original request with cloned request so we can refer to // it in CancelRequest() //t.setModReq(req, req2) john close 2015.11.4 req = req2 /* john close 2015.11.4 b defer func() { // Release req/clone mapping on error if err != nil { t.setModReq(req, nil) } if resp != nil { // Release req/clone mapping on body close/EOF resp.Body = &onEOFReader{ rc: resp.Body, fn: func() { t.setModReq(req, nil) }, } } }() john close 2015.11.4 e */ } } } resp, err = transport.RoundTrip(req) glog.V(logVNum).Infof("transport.RoundTrip url = %v", req.URL.String()) if err == nil && req.Method == "GET" && resp.StatusCode == http.StatusNotModified { return resp, nil // Replace the 304 response with the one from cache, but update with some new headers endToEndHeaders := getEndToEndHeaders(resp.Header) glog.V(logVNum).Infof("endToEndHeaders = %v", endToEndHeaders) for _, header := range endToEndHeaders { cachedResp.Header[header] = resp.Header[header] } cachedResp.Status = fmt.Sprintf("%d %s", http.StatusOK, http.StatusText(http.StatusOK)) cachedResp.StatusCode = http.StatusOK resp = cachedResp } else if (err != nil || (cachedResp != nil && resp.StatusCode >= 500)) && req.Method == "GET" && canStaleOnError(cachedResp.Header, req.Header) { // In case of transport failure and stale-if-error activated, returns cached content // when available cachedResp.Status = fmt.Sprintf("%d %s", http.StatusOK, http.StatusText(http.StatusOK)) cachedResp.StatusCode = http.StatusOK return cachedResp, nil } else { if err != nil || resp.StatusCode != http.StatusOK { t.Cache.Delete(cacheKey) } if err != nil { glog.Errorf("err = %v", err) return nil, err } } } else { reqCacheControl := parseCacheControl(req.Header) if _, ok := reqCacheControl["only-if-cached"]; ok { resp = newGatewayTimeoutResponse(req) } else { resp, err = transport.RoundTrip(req) if err != nil { glog.Errorf("transport.RoundTrip err = %v", err) return nil, err } } } reqCacheControl := parseCacheControl(req.Header) respCacheControl := parseCacheControl(resp.Header) glog.V(logVNum+1).Infof("url = %v, reqCacheControl = %v, respCacheControl = %v", req.URL.String(), reqCacheControl, respCacheControl) if canStore(reqCacheControl, respCacheControl) { for _, varyKey := range headerAllCommaSepValues(resp.Header, "vary") { varyKey = http.CanonicalHeaderKey(varyKey) fakeHeader := "X-Varied-" + varyKey reqValue := req.Header.Get(varyKey) if reqValue != "" { resp.Header.Set(fakeHeader, reqValue) //glog.V(logVNum).Infof("Header.Set fakeHeader = %v, reqValue = %v", fakeHeader, reqValue) } } respBytes, err := httputil.DumpResponse(resp, true) if err == nil { t.Cache.Set(cacheKey, respBytes) } } else { t.Cache.Delete(cacheKey) } return resp, nil }