You are viewing a plain text version of this content. The canonical link for it is here.
Posted to solr-commits@lucene.apache.org by ot...@apache.org on 2008/05/23 22:55:48 UTC

svn commit: r659657 - in /lucene/solr/trunk/src: java/org/apache/solr/handler/ java/org/apache/solr/handler/component/ java/org/apache/solr/request/ test/org/apache/solr/servlet/ webapp/src/org/apache/solr/servlet/ webapp/src/org/apache/solr/servlet/ca...

Author: otis
Date: Fri May 23 13:55:48 2008
New Revision: 659657

URL: http://svn.apache.org/viewvc?rev=659657&view=rev
Log:
SOLR-505 Give RequestHandlers the possiblity to suppress the generation of HTTP caching headers

Modified:
    lucene/solr/trunk/src/java/org/apache/solr/handler/MoreLikeThisHandler.java
    lucene/solr/trunk/src/java/org/apache/solr/handler/SpellCheckerRequestHandler.java
    lucene/solr/trunk/src/java/org/apache/solr/handler/component/SearchHandler.java
    lucene/solr/trunk/src/java/org/apache/solr/request/SolrQueryResponse.java
    lucene/solr/trunk/src/test/org/apache/solr/servlet/CacheHeaderTest.java
    lucene/solr/trunk/src/test/org/apache/solr/servlet/CacheHeaderTestBase.java
    lucene/solr/trunk/src/webapp/src/org/apache/solr/servlet/SolrDispatchFilter.java
    lucene/solr/trunk/src/webapp/src/org/apache/solr/servlet/cache/HttpCacheHeaderUtil.java
    lucene/solr/trunk/src/webapp/src/org/apache/solr/servlet/cache/Method.java

Modified: lucene/solr/trunk/src/java/org/apache/solr/handler/MoreLikeThisHandler.java
URL: http://svn.apache.org/viewvc/lucene/solr/trunk/src/java/org/apache/solr/handler/MoreLikeThisHandler.java?rev=659657&r1=659656&r2=659657&view=diff
==============================================================================
--- lucene/solr/trunk/src/java/org/apache/solr/handler/MoreLikeThisHandler.java (original)
+++ lucene/solr/trunk/src/java/org/apache/solr/handler/MoreLikeThisHandler.java Fri May 23 13:55:48 2008
@@ -79,6 +79,7 @@
   public void handleRequestBody(SolrQueryRequest req, SolrQueryResponse rsp) throws Exception 
   {
     RequestHandlerUtils.addExperimentalFormatWarning( rsp );
+    rsp.setHttpCaching(true);
     
     SolrParams params = req.getParams();
     SolrIndexSearcher searcher = req.getSearcher();

Modified: lucene/solr/trunk/src/java/org/apache/solr/handler/SpellCheckerRequestHandler.java
URL: http://svn.apache.org/viewvc/lucene/solr/trunk/src/java/org/apache/solr/handler/SpellCheckerRequestHandler.java?rev=659657&r1=659656&r2=659657&view=diff
==============================================================================
--- lucene/solr/trunk/src/java/org/apache/solr/handler/SpellCheckerRequestHandler.java (original)
+++ lucene/solr/trunk/src/java/org/apache/solr/handler/SpellCheckerRequestHandler.java Fri May 23 13:55:48 2008
@@ -263,6 +263,7 @@
   @Override
   public void handleRequestBody(SolrQueryRequest req, SolrQueryResponse rsp)
     throws Exception {
+    rsp.setHttpCaching(true);
     SolrParams p = req.getParams();
     String words = p.get("q");
     String cmd = p.get("cmd");

Modified: lucene/solr/trunk/src/java/org/apache/solr/handler/component/SearchHandler.java
URL: http://svn.apache.org/viewvc/lucene/solr/trunk/src/java/org/apache/solr/handler/component/SearchHandler.java?rev=659657&r1=659656&r2=659657&view=diff
==============================================================================
--- lucene/solr/trunk/src/java/org/apache/solr/handler/component/SearchHandler.java (original)
+++ lucene/solr/trunk/src/java/org/apache/solr/handler/component/SearchHandler.java Fri May 23 13:55:48 2008
@@ -126,6 +126,8 @@
 
     final RTimer timer = rb.isDebug() ? new RTimer() : null;
 
+    rsp.setHttpCaching(true);
+    
     if (timer == null) {
       // non-debugging prepare phase
       for( SearchComponent c : components ) {

Modified: lucene/solr/trunk/src/java/org/apache/solr/request/SolrQueryResponse.java
URL: http://svn.apache.org/viewvc/lucene/solr/trunk/src/java/org/apache/solr/request/SolrQueryResponse.java?rev=659657&r1=659656&r2=659657&view=diff
==============================================================================
--- lucene/solr/trunk/src/java/org/apache/solr/request/SolrQueryResponse.java (original)
+++ lucene/solr/trunk/src/java/org/apache/solr/request/SolrQueryResponse.java Fri May 23 13:55:48 2008
@@ -72,6 +72,11 @@
   // error if this is set...
   protected Exception err;
 
+  /**
+   * Should this response be tagged with HTTP caching headers?
+   */
+  protected boolean httpCaching=false;
+  
   /***
    // another way of returning an error
   int errCode;
@@ -199,5 +204,20 @@
   public NamedList getToLog() {
 	  return toLog;
   }
-    
+  
+  /**
+   * Enables or disables the emission of HTTP caching headers for this response.
+   * @param httpCaching true=emit caching headers, false otherwise
+   */
+  public void setHttpCaching(boolean httpCaching) {
+    this.httpCaching=httpCaching;
+  }
+  
+  /**
+   * Should this response emit HTTP caching headers?
+   * @return true=yes emit headers, false otherwise
+   */
+  public boolean isHttpCaching() {
+    return this.httpCaching;
+  }
 }

Modified: lucene/solr/trunk/src/test/org/apache/solr/servlet/CacheHeaderTest.java
URL: http://svn.apache.org/viewvc/lucene/solr/trunk/src/test/org/apache/solr/servlet/CacheHeaderTest.java?rev=659657&r1=659656&r2=659657&view=diff
==============================================================================
--- lucene/solr/trunk/src/test/org/apache/solr/servlet/CacheHeaderTest.java (original)
+++ lucene/solr/trunk/src/test/org/apache/solr/servlet/CacheHeaderTest.java Fri May 23 13:55:48 2008
@@ -16,18 +16,68 @@
  */
 package org.apache.solr.servlet;
 
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.OutputStreamWriter;
+import java.io.Writer;
 import java.util.Date;
 
 import org.apache.commons.httpclient.Header;
 import org.apache.commons.httpclient.HttpMethodBase;
+import org.apache.commons.httpclient.NameValuePair;
 import org.apache.commons.httpclient.util.DateUtil;
 
 /**
  * A test case for the several HTTP cache headers emitted by Solr
  */
 public class CacheHeaderTest extends CacheHeaderTestBase {
-  @Override public String getSolrConfigFilename() { return "solrconfig.xml";  }
+  @Override
+  public String getSolrConfigFilename() {
+    return "solrconfig.xml";
+  }
+
+  protected static final String FILENAME = "cacheheadertest.csv";
+
+  protected static final String CHARSET = "UTF-8";
+
+  protected static final String CONTENTS = "id\n100\n101\n102";
+
+  public void testCacheVetoHandler() throws Exception {
+    File f=makeFile(CONTENTS);
+    HttpMethodBase m=getUpdateMethod("GET");
+    m.setQueryString(new NameValuePair[] { new NameValuePair("stream.file",f.getCanonicalPath())});
+    getClient().executeMethod(m);
+    assertEquals(200, m.getStatusCode());
+    checkVetoHeaders(m);
+  }
   
+  public void testCacheVetoException() throws Exception {
+    HttpMethodBase m = getSelectMethod("GET");
+    // We force an exception from Solr. This should emit "no-cache" HTTP headers
+    m.setQueryString(new NameValuePair[] { new NameValuePair("q", "xyz:solr"),
+        new NameValuePair("qt", "standard") });
+    getClient().executeMethod(m);
+    assertFalse(m.getStatusCode() == 200);
+    checkVetoHeaders(m);
+  }
+
+  protected void checkVetoHeaders(HttpMethodBase m) throws Exception {
+    Header head = m.getResponseHeader("Cache-Control");
+    assertNotNull("We got no Cache-Control header", head);
+    assertEquals("no-cache, no-store", head.getValue());
+
+    head = m.getResponseHeader("Pragma");
+    assertNotNull("We got no Pragma header", head);
+    assertEquals("no-cache", head.getValue());
+
+    head = m.getResponseHeader("Expires");
+    assertNotNull("We got no Expires header", head);
+    Date d = DateUtil.parseDate(head.getValue());
+    assertTrue("We got no Expires header far in the past", System
+        .currentTimeMillis()
+        - d.getTime() > 100000);
+  }
+
   protected void doLastModified(String method) throws Exception {
     // We do a first request to get the last modified
     // This must result in a 200 OK response
@@ -162,8 +212,8 @@
 
       Header head = m.getResponseHeader("Cache-Control");
       assertNull("We got a cache-control header in response to POST", head);
-      
-      head=m.getResponseHeader("Expires");
+
+      head = m.getResponseHeader("Expires");
       assertNull("We got an Expires  header in response to POST", head);
     } else {
       HttpMethodBase m = getSelectMethod(method);
@@ -172,9 +222,26 @@
 
       Header head = m.getResponseHeader("Cache-Control");
       assertNotNull("We got no cache-control header", head);
-      
-      head=m.getResponseHeader("Expires");
-      assertNotNull("We got no Expires header in response",head);
+
+      head = m.getResponseHeader("Expires");
+      assertNotNull("We got no Expires header in response", head);
+    }
+  }
+
+  protected File makeFile(String contents) {
+    return makeFile(contents, CHARSET);
+  }
+
+  protected File makeFile(String contents, String charset) {
+    try {
+      File f=new File(FILENAME);
+      Writer out = new OutputStreamWriter(new FileOutputStream(f),
+          charset);
+      out.write(contents);
+      out.close();
+      return f;
+    } catch (Exception e) {
+      throw new RuntimeException(e);
     }
   }
 }

Modified: lucene/solr/trunk/src/test/org/apache/solr/servlet/CacheHeaderTestBase.java
URL: http://svn.apache.org/viewvc/lucene/solr/trunk/src/test/org/apache/solr/servlet/CacheHeaderTestBase.java?rev=659657&r1=659656&r2=659657&view=diff
==============================================================================
--- lucene/solr/trunk/src/test/org/apache/solr/servlet/CacheHeaderTestBase.java (original)
+++ lucene/solr/trunk/src/test/org/apache/solr/servlet/CacheHeaderTestBase.java Fri May 23 13:55:48 2008
@@ -93,6 +93,20 @@
     return m;
   }
 
+  protected HttpMethodBase getUpdateMethod(String method) {
+    HttpMethodBase m = null;
+    
+    if ("GET".equals(method)) {
+      m=new GetMethod(server.getBaseURL()+"/update/csv");
+    } else if ("POST".equals(method)) {
+      m=new PostMethod(server.getBaseURL()+"/update/csv");      
+    } else if ("HEAD".equals(method)) {
+      m=new HeadMethod(server.getBaseURL()+"/update/csv");      
+    }
+    
+    return m;
+  }
+  
   protected HttpClient getClient() {
     return server.getHttpClient();
   }

Modified: lucene/solr/trunk/src/webapp/src/org/apache/solr/servlet/SolrDispatchFilter.java
URL: http://svn.apache.org/viewvc/lucene/solr/trunk/src/webapp/src/org/apache/solr/servlet/SolrDispatchFilter.java?rev=659657&r1=659656&r2=659657&view=diff
==============================================================================
--- lucene/solr/trunk/src/webapp/src/org/apache/solr/servlet/SolrDispatchFilter.java (original)
+++ lucene/solr/trunk/src/webapp/src/org/apache/solr/servlet/SolrDispatchFilter.java Fri May 23 13:55:48 2008
@@ -258,13 +258,11 @@
             }
 
             final Method reqMethod = Method.getMethod(req.getMethod());
-            if (Method.POST != reqMethod) {
-              HttpCacheHeaderUtil.setCacheControlHeader(config, resp);
-            }
+            HttpCacheHeaderUtil.setCacheControlHeader(config, resp, reqMethod);
             // unless we have been explicitly told not to, do cache validation
             // if we fail cache validation, execute the query
             if (config.getHttpCachingConfig().isNever304() ||
-                !HttpCacheHeaderUtil.doCacheHeaderValidation(solrReq, req, resp)) {
+                !HttpCacheHeaderUtil.doCacheHeaderValidation(solrReq, req, reqMethod, resp)) {
                 SolrQueryResponse solrRsp = new SolrQueryResponse();
                 /* even for HEAD requests, we need to execute the handler to
                  * ensure we don't get an error (and to make sure the correct
@@ -272,6 +270,7 @@
                  * Content-Type)
                  */
                 this.execute( req, handler, solrReq, solrRsp );
+                HttpCacheHeaderUtil.checkHttpCachingVeto(solrRsp, resp, reqMethod);
               // add info to http headers
               //TODO: See SOLR-232 and SOLR-267.  
                 /*try {
@@ -289,7 +288,7 @@
                   // Now write it out
                   QueryResponseWriter responseWriter = core.getQueryResponseWriter(solrReq);
                   response.setContentType(responseWriter.getContentType(solrReq, solrRsp));
-                  if (Method.HEAD != Method.getMethod(req.getMethod())) {
+                  if (Method.HEAD != reqMethod) {
                     if (responseWriter instanceof BinaryQueryResponseWriter) {
                       BinaryQueryResponseWriter binWriter = (BinaryQueryResponseWriter) responseWriter;
                       binWriter.write(response.getOutputStream(), solrReq, solrRsp);

Modified: lucene/solr/trunk/src/webapp/src/org/apache/solr/servlet/cache/HttpCacheHeaderUtil.java
URL: http://svn.apache.org/viewvc/lucene/solr/trunk/src/webapp/src/org/apache/solr/servlet/cache/HttpCacheHeaderUtil.java?rev=659657&r1=659656&r2=659657&view=diff
==============================================================================
--- lucene/solr/trunk/src/webapp/src/org/apache/solr/servlet/cache/HttpCacheHeaderUtil.java (original)
+++ lucene/solr/trunk/src/webapp/src/org/apache/solr/servlet/cache/HttpCacheHeaderUtil.java Fri May 23 13:55:48 2008
@@ -35,6 +35,7 @@
 import org.apache.solr.common.SolrException.ErrorCode;
 import org.apache.solr.search.SolrIndexSearcher;
 import org.apache.solr.request.SolrQueryRequest;
+import org.apache.solr.request.SolrQueryResponse;
 import org.apache.solr.request.SolrRequestHandler;
 
 import org.apache.commons.codec.binary.Base64;
@@ -167,10 +168,16 @@
   /**
    * Set the Cache-Control HTTP header (and Expires if needed)
    * based on the SolrConfig.
+   * @param conf The config of the SolrCore handling this request
+   * @param resp The servlet response object to modify
+   * @param method The request method (GET, POST, ...) used by this request
    */
   public static void setCacheControlHeader(final SolrConfig conf,
-                                           final HttpServletResponse resp) {
-
+                                           final HttpServletResponse resp, final Method method) {
+    // We do not emit HTTP header for POST and OTHER request types
+    if (Method.POST==method || Method.OTHER==method) {
+      return;
+    }
     final String cc = conf.getHttpCachingConfig().getCacheControlHeader();
     if (null != cc) {
       resp.setHeader("Cache-Control", cc);
@@ -202,10 +209,13 @@
    */
   public static boolean doCacheHeaderValidation(final SolrQueryRequest solrReq,
                                                 final HttpServletRequest req,
+                                                final Method reqMethod,
                                                 final HttpServletResponse resp)
     throws IOException {
-
-    final Method reqMethod=Method.getMethod(req.getMethod());
+    
+    if (Method.POST==reqMethod || Method.OTHER==reqMethod) {
+      return false;
+    }
     
     final long lastMod = HttpCacheHeaderUtil.calcLastModified(solrReq);
     final String etag = HttpCacheHeaderUtil.calcEtag(solrReq);
@@ -295,4 +305,42 @@
     }
     return false;
   }
+
+   /**
+   * Checks if the downstream request handler wants to avoid HTTP caching of
+   * the response.
+   * 
+   * @param solrRsp The Solr response object
+   * @param resp The HTTP servlet response object
+   * @param reqMethod The HTTP request type
+   */
+  public static void checkHttpCachingVeto(final SolrQueryResponse solrRsp,
+      HttpServletResponse resp, final Method reqMethod) {
+    // For POST we do nothing. They never get cached
+    if (Method.POST == reqMethod || Method.OTHER == reqMethod) {
+      return;
+    }
+    // If the request handler has not vetoed and there is no
+    // exception silently return
+    if (solrRsp.isHttpCaching() && solrRsp.getException() == null) {
+      return;
+    }
+    
+    // Otherwise we tell the caches that we don't want to cache the response
+    resp.setHeader("Cache-Control", "no-cache, no-store");
+
+    // For HTTP/1.0 proxy caches
+    resp.setHeader("Pragma", "no-cache");
+
+    // This sets the expiry date to a date in the past
+    // As long as no time machines get invented this is safe
+    resp.setHeader("Expires", "Sat, 01 Jan 2000 01:00:00 GMT");
+
+    // We signal "just modified" just in case some broken
+    // proxy cache does not follow the above headers
+    resp.setDateHeader("Last-Modified", System.currentTimeMillis());
+    
+    // We override the ETag with something different
+    resp.setHeader("ETag", '"'+Long.toHexString(System.currentTimeMillis())+'"');
+  } 
 }

Modified: lucene/solr/trunk/src/webapp/src/org/apache/solr/servlet/cache/Method.java
URL: http://svn.apache.org/viewvc/lucene/solr/trunk/src/webapp/src/org/apache/solr/servlet/cache/Method.java?rev=659657&r1=659656&r2=659657&view=diff
==============================================================================
--- lucene/solr/trunk/src/webapp/src/org/apache/solr/servlet/cache/Method.java (original)
+++ lucene/solr/trunk/src/webapp/src/org/apache/solr/servlet/cache/Method.java Fri May 23 13:55:48 2008
@@ -18,24 +18,13 @@
 package org.apache.solr.servlet.cache;
 
 public enum Method {
-  GET("GET"), POST("POST"), HEAD("HEAD"), OTHER("");
-
-  private final String method;
-
-  Method(String method) {
-    this.method = method.intern();
-  }
+  GET, POST, HEAD, OTHER;
 
   public static Method getMethod(String method) {
-    method = method.toUpperCase().intern();
-
-    for (Method m : Method.values()) {
-      // we can use == because we interned the String objects
-      if (m.method==method) {
-        return m;
-      }
+    try {
+      return Method.valueOf(method.toUpperCase());
+    } catch (Exception e) {
+      return OTHER;
     }
-
-    return OTHER;
   }
 }