cachefilter.java
来自「很棒的web服务器源代码」· Java 代码 · 共 892 行 · 第 1/2 页
JAVA
892 行
// CacheFilter.java// $Id: CacheFilter.java,v 1.92 2003/04/02 19:08:09 ylafon Exp $// (c) COPYRIGHT MIT, INRIA and Keio, 1999.// Please first read the full copyright statement in file COPYRIGHT.htmlpackage org.w3c.www.protocol.http.cache;import java.io.File;import java.lang.reflect.Method;import java.lang.reflect.InvocationTargetException;import java.net.URL;import java.net.MalformedURLException;import java.util.Vector;import java.util.Hashtable;import org.w3c.util.ObservableProperties;import org.w3c.util.PropertyMonitoring;import org.w3c.util.URLUtils;import org.w3c.www.protocol.http.HttpException;import org.w3c.www.protocol.http.HttpManager;import org.w3c.www.protocol.http.PropRequestFilter;import org.w3c.www.protocol.http.PropRequestFilterException;import org.w3c.www.protocol.http.Reply;import org.w3c.www.protocol.http.Request;import org.w3c.www.protocol.http.RequestFilter;import org.w3c.www.http.HTTP;import org.w3c.www.http.HttpCacheControl;import org.w3c.www.http.HttpEntityMessage;import org.w3c.www.http.HttpFactory;import org.w3c.www.http.HttpInvalidValueException;import org.w3c.www.http.HttpMessage;import org.w3c.www.http.HttpReplyMessage;import org.w3c.www.http.HttpRequestMessage;import org.w3c.www.http.HttpSetCookieList;import org.w3c.www.http.HttpWarning;public class CacheFilter implements PropRequestFilter, PropertyMonitoring { // serializer class public static final String SERIALIZER_P = "org.w3c.www.protocol.http.cache.serializerclass"; // sweeper class public static final String SWEEPER_P = "org.w3c.www.protocol.http.cache.sweeperclass"; // validator class public static final String VALIDATOR_P = "org.w3c.www.protocol.http.cache.validatorclass"; /** * Name of the property enabling the connected/disconnected mode */ public static final String CACHE_CONNECTED_P = "org.w3c.www.protocol.http.cache.connected"; /** * Name of the property indicating if this cache is shared. * <p>This property defaults to <strong>true</strong>. */ public static final String SHARED_P = "org.w3c.www.protocol.http.cache.shared"; /** * The name of the properties indicating the size of the cache (in bytes). * This property will give the value of the disk-based cache size. This * value only takes into account the size of the entities saved, not * the size of the associated headers, and not the physical size on the * disc. * <p>This property defaults to <strong>5000000</strong> bytes. */ public static final String CACHE_SIZE_P = "org.w3c.www.protocol.http.cache.size"; /** * Name of the property indicating if the cache is in debug mode. * <p>This property defaults to <strong>false</strong>. */ public static final String DEBUG_P = "org.w3c.www.protocol.http.cache.debug"; /** * The state used to disable that filter per request. Also set by the cache * if the request cannot be fullfilled by caches, as detected by this * filter. */ public final static String STATE_NOCACHE = "org.w3c.www.protocol.http.cache.dont"; /** * Name of the request state used to collect warnings. */ public final static String STATE_WARNINGS = "org.w3c.www.protocol.http.cache.CacheFilter.warns"; /** * Name of the request state used tokeep track of original request */ public final static String STATE_ORIGREQ = "org.w3c.www.protocol.http.cache.CacheFilter.origreq"; /** * Name of the request state that marks a request as being a revalidation. */ public final static String STATE_REVALIDATION = "org.w3c.www.protocol.http.cache.revalidation"; /** * The HTTP warning used to notify of a disconnected cache. */ protected static HttpWarning WARN_DISCONNECTED = null; /** * The HTTP warning used to mark invalid entries */ protected static HttpWarning WARN_STALE = null; /** * The HTTP warning used to indicate a heuristic expiration time. */ protected static HttpWarning WARN_HEURISTIC = null; static { // Build the std "disconnected" warning HttpWarning w = null; w = HttpFactory.makeWarning(HttpWarning.DISCONNECTED_OPERATION); w.setAgent("Jigsaw"); w.setText("The required cached resource is stale."); WARN_DISCONNECTED = w; // Build the stale std warning w = HttpFactory.makeWarning(HttpWarning.STALE); w.setAgent("Jigsaw"); w.setText("The returned entry is stale."); WARN_STALE = w; // Build the heuristic expiration warning: w = HttpFactory.makeWarning(HttpWarning.HEURISTIC_EXPIRATION); w.setAgent("Jigsaw"); w.setText("Heuristic expiration time used on this entry."); WARN_HEURISTIC = w; } /** * The properties we initialized ourself from. */ protected ObservableProperties props = null; // our validator protected CacheValidator validator; // our caches tore protected CacheStore store; // our cache sweeper protected CacheSweeper sweeper; // our cache serializer protected CacheSerializer serializer; // is the cache connected? protected boolean connected = true; // is the cache shared? protected boolean shared = true; // The cache size protected long size = 20971520; // 20Mo is the default protected File directory = null; // should ew debug this? protected boolean debug = false; // the hastable of not downloaded resources protected Hashtable precache = new Hashtable(10); // the hastable of the URI to be downloaded protected Hashtable uritable = new Hashtable(10); /** * return the cache sweeper used by the cache * @return an instance of CacheSweeper */ public CacheSweeper getSweeper() { return sweeper; } /** * return the serializer used by the cache * @return an instance of Serializer */ public CacheSerializer getSerializer() { return serializer; } /** * return the cache validator used by the cache * @return an instance of CacheValidator */ public CacheValidator getValidator() { return validator; } /** * is the cache shared? * @return a boolean, true if the cache is shared */ public boolean isShared() { return shared; } /** * is the cache connected? * @return a boolean, true if the cache is connected */ public boolean isConnected() { return connected; } /** * Display some output, related to the request (used for debugging) */ protected final void trace(Request request, String msg) { System.out.println(request.getURL()+": "+msg); } /** * Add a warning, to be emitted at reply time. * The cache filter keeps track, through a specific piece of request state * of the warnings to be emitted at reply time (if any). * <p>During request processing, cached resources can add any kind * of warnings, which will be collected and forwarded back to the reply. * @param request The request being process, and whose reply requires * some warnings. * @param warning The warning to be emitted if ever we use the cache * filter to answer the above request. */ protected void addWarning(Request request, HttpWarning warning) { Vector vw = (Vector) request.getState(STATE_WARNINGS); if ( vw == null ) { vw = new Vector(4); request.setState(STATE_WARNINGS, vw); } vw.addElement(warning); } /** * Copy all warnings colllected into the given reply. * This method collects all HTTP warnings saved during request processing * and create (if needed) the approporiate warning header in the given * reply. * @param request The request that has been processed by the cache filter. * @param reply The reply that has been constructed from the cache. * @see #addWarning */ protected final void setWarnings(Request request, Reply reply) { Vector vw = (Vector) request.getState(STATE_WARNINGS); if ( vw == null ) return; HttpWarning ws[] = new HttpWarning[vw.size()]; vw.copyInto(ws); reply.setWarning(ws); } /** * check if we can use the cache or not for this request * It marks the request as being not cachable if false. * @param a request, the incoming client-side request * @return a boolean, true if we can use the cache */ public boolean canUseCache(Request req) { // RFC2616: 14.32 Cache-Control equivalence of Pragma: no-cache // RFC2616: 14.9.2 no-store directive // RFC2616: 14.9.4 End-to-end reload if (req.hasPragma("no-cache") || (req.getNoCache() != null)) { req.setState(CacheState.STATE_NOCACHE, Boolean.TRUE); return false; } if (req.checkNoStore()) { req.setState(CacheState.STATE_STORABLE, Boolean.FALSE); return false; } String method = req.getMethod(); if (!method.equals("GET") && !method.equals("HEAD") ) { req.setState(CacheState.STATE_NOCACHE, Boolean.TRUE); return false; } return true; } /** * Checks if, according to the headers of the reply, an entity may * be cached or not, it decorates also the reply * @param a request, the client side request * @param a reply, the client side reply * @return a boolean, true if the resource can be cached */ public boolean canCache(Request req, Reply rep) { String method = req.getMethod(); // only cache GET and HEAD if (!method.equals("GET") /* FIXME && !method.equals("HEAD")*/ ) { rep.setState(CacheState.STATE_CACHABLE, Boolean.FALSE); return false; } // don't cache HTTP/0.9 replies for now if (req.getMajorVersion() == 0) { rep.setState(CacheState.STATE_CACHABLE, Boolean.FALSE); return false; } // Ugly Hack for lame cookies if (rep.getSetCookie() != null) { rep.setState(CacheState.STATE_CACHABLE, Boolean.FALSE); return false; } // RFC2616: 13.4 Response cacheability int status = rep.getStatus(); if ( (status != HTTP.OK) && (status != HTTP.NON_AUTHORITATIVE_INFORMATION) && (status != HTTP.PARTIAL_CONTENT) && (status != HTTP.MULTIPLE_CHOICE) && (status != HTTP.MOVED_PERMANENTLY) && (status != HTTP.GONE)) { rep.setState(CacheState.STATE_CACHABLE, Boolean.FALSE); return false; } HttpCacheControl repcc = null; try { repcc = rep.getCacheControl(); } catch (HttpInvalidValueException ex) { // invalid header, be safe and avoid caching repcc = HttpFactory.parseCacheControl("no-cache"); rep.setCacheControl(repcc); } // first check if we are told that we can cache the resource if (repcc != null) { // RFC2616: 14.9.1 Cache-Control: public overrides everything if (repcc.checkPublic()) { rep.setState(CacheState.STATE_CACHABLE, Boolean.TRUE); return true; } // RFC2616: 14.9.1 Cache-Control: private // We are not handling for now the field names that may be // associated if (isShared() && (repcc.getPrivate() != null)) { rep.setState(CacheState.STATE_CACHABLE, Boolean.FALSE); return false; } } // RFC2616: 14.9.1 no-cache, note that we are not using // the optional field-names (it is a MAY) if (rep.getNoCache() != null) { rep.setState(CacheState.STATE_CACHABLE, Boolean.FALSE); return false; } // HTTP/1.[01] Pragma no-cache RFC2616: 14.32 Cache-Control equivalence if (rep.hasPragma("no-cache")) { rep.setState(CacheState.STATE_CACHABLE, Boolean.FALSE); return false; } // Now check for URI and HTTP/1.0, // RFC2616: 13.9 HTTP/1.0 with ? in the URI and no Expires should not // be cached. if ((req.getURL().getFile().indexOf('?') != -1) && ((rep.getMajorVersion() == 1) && (rep.getMinorVersion() == 0)) && (rep.getExpires() == -1)) { rep.setState(CacheState.STATE_CACHABLE, Boolean.FALSE); return false; } // Do we have an authentication? // without a cache-control, we deny caching for now. if (req.hasAuthorization()) { rep.setState(CacheState.STATE_CACHABLE, Boolean.FALSE); return false; } // by default, it is cacheable rep.setState(CacheState.STATE_CACHABLE, Boolean.TRUE); return true; } /** * Checks if, according to the headers of a reply we can store * the resource. * Note that a resource may be cachable, but not storable (memory cache) * although is MUST do its best to get rid of it asap, in our case we just * don't store it! * @param a request, the client side request * @param a reply, the client side reply * @return a boolean, true if the resource can be stored by the cache */ public boolean canStore(Request req, Reply rep) { // RFC2616: 14.9.2 What can be stored... if (req.checkNoStore() || rep.checkNoStore()) { rep.setState(CacheState.STATE_STORABLE, Boolean.FALSE); return false; } rep.setState(CacheState.STATE_CACHABLE, Boolean.TRUE); return true; } /** * Modify a request to ask for a revalidation * @param the resource to be revalidated * @param request, the original request to be modified */ protected Request setRequestRevalidation(CachedResource res, Request req) { try { return store.getCachedResource(res).setRequestRevalidation(req); } catch (InvalidCacheException ex) { // should never happen as we know it is in the cache } return null; } /** * The request pre-processing hook. * Before each request is launched, all filters will be called back through * this method. They will generally set up additional request header * fields to enhance the request. * @param request The request that is about to be launched. * @return An instance of Reply if the filter could handle the request, * or <strong>null</strong> if processing should continue normally. * @exception HttpException If the filter is supposed to fulfill the * request, but some error happened during that processing. */ public Reply ingoingFilter(Request request) throws HttpException { // can we use the cache? if (!canUseCache(request)) { if (debug) { trace(request, "*** Can't use cache"); } // we will invalidate this resource, will do that only // on real entity resource, not on negotiated ones if (connected) { CachedResource res = null; EntityCachedResource invalidRes = null; try { URL _ru = request.getURL(); String requrl = URLUtils.normalize(_ru).toExternalForm(); res = store.getCachedResourceReference(requrl); if (res != null) { invalidRes = (EntityCachedResource) res.lookupResource(request); } } catch (InvalidCacheException ex) { invalidRes = null; } if (invalidRes != null) { invalidRes.setWillRevalidate(true); } request.setState(STATE_NOCACHE, Boolean.TRUE); return null; } else { // disconnected, abort now! Reply reply = request.makeReply(HTTP.GATEWAY_TIMEOUT); reply.setContent("The cache cannot be use for " + "<p><code>"+request.getMethod()+"</code> " + "<strong>"+request.getURL()+"</strong>" + ". <p>It is disconnected."); return reply; } } // let's try to get the resource!
⌨️ 快捷键说明
复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?