Ejemplo n.º 1
0
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
}
Ejemplo n.º 2
0
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
}
Ejemplo n.º 3
0
// 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
}