/** * Copyright (C) 2012-2014 Gist Labs, LLC. (http://gistlabs.com) * * This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ package com.gistlabs.mechanize.cache; import org.apache.http.HttpResponse; import org.apache.http.client.methods.HttpUriRequest; import org.apache.http.protocol.HttpContext; import com.gistlabs.mechanize.cache.api.CacheEntry; import com.gistlabs.mechanize.cache.api.HttpCache; import com.gistlabs.mechanize.cache.inMemory.InMemoryCacheEntry; import com.gistlabs.mechanize.cache.inMemory.InMemoryHttpCache; import com.gistlabs.mechanize.filters.MechanizeChainFilter; import com.gistlabs.mechanize.filters.MechanizeFilter; /** * Support for cache and conditional HTTP request/response handling * * Includes support for the following HTTP Headers to support caching: Cache-Control, Expires, Date, Age * Includes support for the following HTTP Headers to support conditions: Last-Modified, ETag, If-Modified-Since, If-None-Match * */ public class HttpCacheFilter implements MechanizeChainFilter { final HttpCache cache; public HttpCacheFilter() { this(new InMemoryHttpCache()); } public HttpCacheFilter(final HttpCache cache) { this.cache = cache; } @Override public HttpResponse execute(final HttpUriRequest request, final HttpContext context, final MechanizeFilter chain) { String method = request.getMethod().toUpperCase(); if (method.equals("GET")) return executeGET(request, context, chain); else if (method.equals("HEAD")) return executeHEAD(request, context, chain); else return executeOther(request, context, chain); } public HttpResponse executeOther(final HttpUriRequest request, final HttpContext context, final MechanizeFilter chain) { String uri = request.getURI().toString(); invalidate(uri, request); return chain.execute(request, context); } public HttpResponse executeGET(final HttpUriRequest request, final HttpContext context, final MechanizeFilter chain) { String uri = request.getURI().toString(); CacheEntry previous = cache.get(uri); if (previous!=null && previous.isValid()) return previous.getResponse(); if (previous!=null) previous.prepareConditionalGet(request); HttpResponse response = chain.execute(request, context); // call the chain if (response.getStatusLine().getStatusCode()==304) // not modified return previous.updateCacheValues(response).getResponse(); CacheEntry maybe = generateCacheEntry(request, response); if (maybe.isCacheable()) store(uri, previous, maybe); return response; } protected InMemoryCacheEntry generateCacheEntry( final HttpUriRequest request, HttpResponse response) { return new InMemoryCacheEntry(request, response); } public HttpResponse executeHEAD(final HttpUriRequest request, final HttpContext context, final MechanizeFilter chain) { String uri = request.getURI().toString(); CacheEntry previous = cache.get(uri); if (previous!=null && previous.isValid()) return previous.head(); if (previous!=null) previous.prepareConditionalGet(request); HttpResponse response = chain.execute(request, context); // call the chain if (previous!=null) previous.updateCacheValues(response); return response; } protected void invalidate(final String uri, final HttpUriRequest request) { cache.remove(uri); } protected boolean store(final String uri, final CacheEntry cachedValue, final CacheEntry maybe) { if (cachedValue==null) return cache.putIfAbsent(uri, maybe); else return cache.replace(uri, cachedValue, maybe); } }