mirror of
https://github.com/apache/lucene.git
synced 2025-02-28 13:29:26 +00:00
SOLR-505 Give RequestHandlers the possiblity to suppress the generation of HTTP caching headers
git-svn-id: https://svn.apache.org/repos/asf/lucene/solr/trunk@659657 13f79535-47bb-0310-9956-ffa450edef68
This commit is contained in:
parent
a0de6f7b9f
commit
5ba95af1bb
@ -79,6 +79,7 @@ public class MoreLikeThisHandler extends RequestHandlerBase
|
|||||||
public void handleRequestBody(SolrQueryRequest req, SolrQueryResponse rsp) throws Exception
|
public void handleRequestBody(SolrQueryRequest req, SolrQueryResponse rsp) throws Exception
|
||||||
{
|
{
|
||||||
RequestHandlerUtils.addExperimentalFormatWarning( rsp );
|
RequestHandlerUtils.addExperimentalFormatWarning( rsp );
|
||||||
|
rsp.setHttpCaching(true);
|
||||||
|
|
||||||
SolrParams params = req.getParams();
|
SolrParams params = req.getParams();
|
||||||
SolrIndexSearcher searcher = req.getSearcher();
|
SolrIndexSearcher searcher = req.getSearcher();
|
||||||
|
@ -263,6 +263,7 @@ public class SpellCheckerRequestHandler extends RequestHandlerBase implements So
|
|||||||
@Override
|
@Override
|
||||||
public void handleRequestBody(SolrQueryRequest req, SolrQueryResponse rsp)
|
public void handleRequestBody(SolrQueryRequest req, SolrQueryResponse rsp)
|
||||||
throws Exception {
|
throws Exception {
|
||||||
|
rsp.setHttpCaching(true);
|
||||||
SolrParams p = req.getParams();
|
SolrParams p = req.getParams();
|
||||||
String words = p.get("q");
|
String words = p.get("q");
|
||||||
String cmd = p.get("cmd");
|
String cmd = p.get("cmd");
|
||||||
|
@ -126,6 +126,8 @@ public class SearchHandler extends RequestHandlerBase implements SolrCoreAware
|
|||||||
|
|
||||||
final RTimer timer = rb.isDebug() ? new RTimer() : null;
|
final RTimer timer = rb.isDebug() ? new RTimer() : null;
|
||||||
|
|
||||||
|
rsp.setHttpCaching(true);
|
||||||
|
|
||||||
if (timer == null) {
|
if (timer == null) {
|
||||||
// non-debugging prepare phase
|
// non-debugging prepare phase
|
||||||
for( SearchComponent c : components ) {
|
for( SearchComponent c : components ) {
|
||||||
|
@ -72,6 +72,11 @@ public class SolrQueryResponse {
|
|||||||
// error if this is set...
|
// error if this is set...
|
||||||
protected Exception err;
|
protected Exception err;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Should this response be tagged with HTTP caching headers?
|
||||||
|
*/
|
||||||
|
protected boolean httpCaching=false;
|
||||||
|
|
||||||
/***
|
/***
|
||||||
// another way of returning an error
|
// another way of returning an error
|
||||||
int errCode;
|
int errCode;
|
||||||
@ -199,5 +204,20 @@ public class SolrQueryResponse {
|
|||||||
public NamedList getToLog() {
|
public NamedList getToLog() {
|
||||||
return toLog;
|
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;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -16,18 +16,68 @@
|
|||||||
*/
|
*/
|
||||||
package org.apache.solr.servlet;
|
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 java.util.Date;
|
||||||
|
|
||||||
import org.apache.commons.httpclient.Header;
|
import org.apache.commons.httpclient.Header;
|
||||||
import org.apache.commons.httpclient.HttpMethodBase;
|
import org.apache.commons.httpclient.HttpMethodBase;
|
||||||
|
import org.apache.commons.httpclient.NameValuePair;
|
||||||
import org.apache.commons.httpclient.util.DateUtil;
|
import org.apache.commons.httpclient.util.DateUtil;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A test case for the several HTTP cache headers emitted by Solr
|
* A test case for the several HTTP cache headers emitted by Solr
|
||||||
*/
|
*/
|
||||||
public class CacheHeaderTest extends CacheHeaderTestBase {
|
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 {
|
protected void doLastModified(String method) throws Exception {
|
||||||
// We do a first request to get the last modified
|
// We do a first request to get the last modified
|
||||||
// This must result in a 200 OK response
|
// This must result in a 200 OK response
|
||||||
@ -162,8 +212,8 @@ public class CacheHeaderTest extends CacheHeaderTestBase {
|
|||||||
|
|
||||||
Header head = m.getResponseHeader("Cache-Control");
|
Header head = m.getResponseHeader("Cache-Control");
|
||||||
assertNull("We got a cache-control header in response to POST", head);
|
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);
|
assertNull("We got an Expires header in response to POST", head);
|
||||||
} else {
|
} else {
|
||||||
HttpMethodBase m = getSelectMethod(method);
|
HttpMethodBase m = getSelectMethod(method);
|
||||||
@ -172,9 +222,26 @@ public class CacheHeaderTest extends CacheHeaderTestBase {
|
|||||||
|
|
||||||
Header head = m.getResponseHeader("Cache-Control");
|
Header head = m.getResponseHeader("Cache-Control");
|
||||||
assertNotNull("We got no cache-control header", head);
|
assertNotNull("We got no cache-control header", head);
|
||||||
|
|
||||||
head=m.getResponseHeader("Expires");
|
head = m.getResponseHeader("Expires");
|
||||||
assertNotNull("We got no Expires header in response",head);
|
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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -93,6 +93,20 @@ public abstract class CacheHeaderTestBase extends SolrExampleTestBase {
|
|||||||
return m;
|
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() {
|
protected HttpClient getClient() {
|
||||||
return server.getHttpClient();
|
return server.getHttpClient();
|
||||||
}
|
}
|
||||||
|
@ -258,13 +258,11 @@ public class SolrDispatchFilter implements Filter
|
|||||||
}
|
}
|
||||||
|
|
||||||
final Method reqMethod = Method.getMethod(req.getMethod());
|
final Method reqMethod = Method.getMethod(req.getMethod());
|
||||||
if (Method.POST != reqMethod) {
|
HttpCacheHeaderUtil.setCacheControlHeader(config, resp, reqMethod);
|
||||||
HttpCacheHeaderUtil.setCacheControlHeader(config, resp);
|
|
||||||
}
|
|
||||||
// unless we have been explicitly told not to, do cache validation
|
// unless we have been explicitly told not to, do cache validation
|
||||||
// if we fail cache validation, execute the query
|
// if we fail cache validation, execute the query
|
||||||
if (config.getHttpCachingConfig().isNever304() ||
|
if (config.getHttpCachingConfig().isNever304() ||
|
||||||
!HttpCacheHeaderUtil.doCacheHeaderValidation(solrReq, req, resp)) {
|
!HttpCacheHeaderUtil.doCacheHeaderValidation(solrReq, req, reqMethod, resp)) {
|
||||||
SolrQueryResponse solrRsp = new SolrQueryResponse();
|
SolrQueryResponse solrRsp = new SolrQueryResponse();
|
||||||
/* even for HEAD requests, we need to execute the handler to
|
/* even for HEAD requests, we need to execute the handler to
|
||||||
* ensure we don't get an error (and to make sure the correct
|
* ensure we don't get an error (and to make sure the correct
|
||||||
@ -272,6 +270,7 @@ public class SolrDispatchFilter implements Filter
|
|||||||
* Content-Type)
|
* Content-Type)
|
||||||
*/
|
*/
|
||||||
this.execute( req, handler, solrReq, solrRsp );
|
this.execute( req, handler, solrReq, solrRsp );
|
||||||
|
HttpCacheHeaderUtil.checkHttpCachingVeto(solrRsp, resp, reqMethod);
|
||||||
// add info to http headers
|
// add info to http headers
|
||||||
//TODO: See SOLR-232 and SOLR-267.
|
//TODO: See SOLR-232 and SOLR-267.
|
||||||
/*try {
|
/*try {
|
||||||
@ -289,7 +288,7 @@ public class SolrDispatchFilter implements Filter
|
|||||||
// Now write it out
|
// Now write it out
|
||||||
QueryResponseWriter responseWriter = core.getQueryResponseWriter(solrReq);
|
QueryResponseWriter responseWriter = core.getQueryResponseWriter(solrReq);
|
||||||
response.setContentType(responseWriter.getContentType(solrReq, solrRsp));
|
response.setContentType(responseWriter.getContentType(solrReq, solrRsp));
|
||||||
if (Method.HEAD != Method.getMethod(req.getMethod())) {
|
if (Method.HEAD != reqMethod) {
|
||||||
if (responseWriter instanceof BinaryQueryResponseWriter) {
|
if (responseWriter instanceof BinaryQueryResponseWriter) {
|
||||||
BinaryQueryResponseWriter binWriter = (BinaryQueryResponseWriter) responseWriter;
|
BinaryQueryResponseWriter binWriter = (BinaryQueryResponseWriter) responseWriter;
|
||||||
binWriter.write(response.getOutputStream(), solrReq, solrRsp);
|
binWriter.write(response.getOutputStream(), solrReq, solrRsp);
|
||||||
|
@ -35,6 +35,7 @@ import org.apache.solr.common.SolrException;
|
|||||||
import org.apache.solr.common.SolrException.ErrorCode;
|
import org.apache.solr.common.SolrException.ErrorCode;
|
||||||
import org.apache.solr.search.SolrIndexSearcher;
|
import org.apache.solr.search.SolrIndexSearcher;
|
||||||
import org.apache.solr.request.SolrQueryRequest;
|
import org.apache.solr.request.SolrQueryRequest;
|
||||||
|
import org.apache.solr.request.SolrQueryResponse;
|
||||||
import org.apache.solr.request.SolrRequestHandler;
|
import org.apache.solr.request.SolrRequestHandler;
|
||||||
|
|
||||||
import org.apache.commons.codec.binary.Base64;
|
import org.apache.commons.codec.binary.Base64;
|
||||||
@ -167,10 +168,16 @@ public final class HttpCacheHeaderUtil {
|
|||||||
/**
|
/**
|
||||||
* Set the Cache-Control HTTP header (and Expires if needed)
|
* Set the Cache-Control HTTP header (and Expires if needed)
|
||||||
* based on the SolrConfig.
|
* 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,
|
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();
|
final String cc = conf.getHttpCachingConfig().getCacheControlHeader();
|
||||||
if (null != cc) {
|
if (null != cc) {
|
||||||
resp.setHeader("Cache-Control", cc);
|
resp.setHeader("Cache-Control", cc);
|
||||||
@ -202,10 +209,13 @@ public final class HttpCacheHeaderUtil {
|
|||||||
*/
|
*/
|
||||||
public static boolean doCacheHeaderValidation(final SolrQueryRequest solrReq,
|
public static boolean doCacheHeaderValidation(final SolrQueryRequest solrReq,
|
||||||
final HttpServletRequest req,
|
final HttpServletRequest req,
|
||||||
|
final Method reqMethod,
|
||||||
final HttpServletResponse resp)
|
final HttpServletResponse resp)
|
||||||
throws IOException {
|
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 long lastMod = HttpCacheHeaderUtil.calcLastModified(solrReq);
|
||||||
final String etag = HttpCacheHeaderUtil.calcEtag(solrReq);
|
final String etag = HttpCacheHeaderUtil.calcEtag(solrReq);
|
||||||
@ -295,4 +305,42 @@ public final class HttpCacheHeaderUtil {
|
|||||||
}
|
}
|
||||||
return false;
|
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())+'"');
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -18,24 +18,13 @@
|
|||||||
package org.apache.solr.servlet.cache;
|
package org.apache.solr.servlet.cache;
|
||||||
|
|
||||||
public enum Method {
|
public enum Method {
|
||||||
GET("GET"), POST("POST"), HEAD("HEAD"), OTHER("");
|
GET, POST, HEAD, OTHER;
|
||||||
|
|
||||||
private final String method;
|
|
||||||
|
|
||||||
Method(String method) {
|
|
||||||
this.method = method.intern();
|
|
||||||
}
|
|
||||||
|
|
||||||
public static Method getMethod(String method) {
|
public static Method getMethod(String method) {
|
||||||
method = method.toUpperCase().intern();
|
try {
|
||||||
|
return Method.valueOf(method.toUpperCase());
|
||||||
for (Method m : Method.values()) {
|
} catch (Exception e) {
|
||||||
// we can use == because we interned the String objects
|
return OTHER;
|
||||||
if (m.method==method) {
|
|
||||||
return m;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return OTHER;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user