Author: gagan
Date: Tue Feb 8 09:26:10 2011
New Revision: 1068301
URL: http://svn.apache.org/viewvc?rev=1068301&view=rev
Log:
Patch by nikhilmadan23 | Issue 4047043: Storing Strict No-Cache responses in
AbstractHttpCache | http://codereview.appspot.com/4047043/
Modified:
shindig/trunk/java/common/conf/shindig.properties
shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/http/AbstractHttpCache.java
shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/http/DefaultRequestPipeline.java
shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/http/HttpResponseBuilder.java
shindig/trunk/java/gadgets/src/test/java/org/apache/shindig/gadgets/http/AbstractHttpCacheTest.java
shindig/trunk/java/gadgets/src/test/java/org/apache/shindig/gadgets/http/HttpResponseBuilderTest.java
Modified: shindig/trunk/java/common/conf/shindig.properties
URL:
http://svn.apache.org/viewvc/shindig/trunk/java/common/conf/shindig.properties?rev=1068301&r1=1068300&r2=1068301&view=diff
==============================================================================
--- shindig/trunk/java/common/conf/shindig.properties (original)
+++ shindig/trunk/java/common/conf/shindig.properties Tue Feb 8 09:26:10 2011
@@ -88,6 +88,10 @@ shindig.template-rewrite.extension-tag-n
shindig.cache.http.defaultTtl=3600000
shindig.cache.http.negativeCacheTtl=60000
+# The max-age with which strict no-cache resources should be stored in cache.
The default value is
+# -1, which indicates that strict no-cache resources should never be stored in
cache.
+shindig.cache.http.strict-no-cache-resource.max-age=-1
+
# A default refresh interval for XML files, since there is no natural way for
developers to
# specify this value, and most HTTP responses don't include good cache control
headers.
shindig.cache.xml.refreshInterval=300000
Modified:
shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/http/AbstractHttpCache.java
URL:
http://svn.apache.org/viewvc/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/http/AbstractHttpCache.java?rev=1068301&r1=1068300&r2=1068301&view=diff
==============================================================================
---
shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/http/AbstractHttpCache.java
(original)
+++
shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/http/AbstractHttpCache.java
Tue Feb 8 09:26:10 2011
@@ -17,8 +17,12 @@
*/
package org.apache.shindig.gadgets.http;
+import com.google.common.annotations.VisibleForTesting;
import com.google.common.base.Objects;
import com.google.common.base.Preconditions;
+import com.google.inject.Inject;
+import com.google.inject.name.Named;
+
import org.apache.shindig.auth.SecurityToken;
import org.apache.shindig.gadgets.AuthType;
import org.apache.shindig.gadgets.uri.UriCommon;
@@ -27,6 +31,10 @@ import org.apache.shindig.gadgets.uri.Ur
* Base class for content caches. Defines cache expiration rules and
* and restrictions on allowed content.
*
+ * Note that in the case where strictNoCacheResourceTtlInSeconds is
non-negative, strict no-cache
+ * resources are stored in the cache. In this case, only the
Cache-Control/Pragma headers are stored
+ * and not the original content or other headers.
+ *
* Implementations that override this are discouraged from using custom cache
keys unless there is
* actually customization in the request object itself. It is highly
recommended that you still
* use {@link #createKey} in the base class and append any custom data to the
end of the key instead
@@ -37,6 +45,13 @@ public abstract class AbstractHttpCache
private static final String RESIZE_WIDTH =
UriCommon.Param.RESIZE_WIDTH.getKey();
private static final String RESIZE_QUALITY =
UriCommon.Param.RESIZE_QUALITY.getKey();
+ // TTL to use for strict no-cache response. A value of -1 indicates that a
strict no-cache
+ // resource is never cached.
+ static final long DEFAULT_STRICT_NO_CACHE_RESOURCE_TTL_SEC = -1;
+
+ @Inject(optional = true)
@Named("shindig.cache.http.strict-no-cache-resource.max-age")
+ private long strictNoCacheResourceTtlInSeconds =
DEFAULT_STRICT_NO_CACHE_RESOURCE_TTL_SEC;
+
// Implement these methods to create a concrete HttpCache class.
protected abstract HttpResponse getResponseImpl(String key);
protected abstract void addResponseImpl(String key, HttpResponse response);
@@ -46,7 +61,8 @@ public abstract class AbstractHttpCache
if (isCacheable(request)) {
String keyString = createKey(request);
HttpResponse cached = getResponseImpl(keyString);
- if (responseStillUsable(cached)) {
+ if (responseStillUsable(cached) &&
+ (!cached.isStrictNoCache() || strictNoCacheResourceTtlInSeconds >
0)) {
return cached;
}
}
@@ -54,21 +70,34 @@ public abstract class AbstractHttpCache
}
public boolean addResponse(HttpRequest request, HttpResponse response) {
- if (isCacheable(request, response)) {
- // Both are cacheable. Check for forced cache TTL overrides.
- HttpResponseBuilder responseBuilder = new HttpResponseBuilder(response);
- int forcedTtl = request.getCacheTtl();
- if (forcedTtl != -1) {
- responseBuilder.setCacheTtl(forcedTtl);
+ HttpResponseBuilder responseBuilder;
+ boolean storeStrictNoCacheResources = (strictNoCacheResourceTtlInSeconds
>= 0);
+ if (isCacheable(request, response, storeStrictNoCacheResources)) {
+ if (storeStrictNoCacheResources && response.isStrictNoCache()) {
+ responseBuilder = buildStrictNoCacheHttpResponse(request, response);
+ } else {
+ responseBuilder = new HttpResponseBuilder(response);
}
-
- response = responseBuilder.create();
- String keyString = createKey(request);
- addResponseImpl(keyString, response);
- return true;
+ } else {
+ return false;
+ }
+ int forcedTtl = request.getCacheTtl();
+ if (forcedTtl != -1) {
+ responseBuilder.setCacheTtl(forcedTtl);
}
+ response = responseBuilder.create();
+ String keyString = createKey(request);
+ addResponseImpl(keyString, response);
+ return true;
+ }
- return false;
+ @VisibleForTesting
+ HttpResponseBuilder buildStrictNoCacheHttpResponse(HttpRequest request,
HttpResponse response) {
+ HttpResponseBuilder responseBuilder = new HttpResponseBuilder();
+ responseBuilder.setHeader("Cache-Control",
response.getHeader("Cache-Control"));
+ responseBuilder.setHeader("Pragma", response.getHeader("Pragma"));
+ responseBuilder.setCacheControlMaxAge(strictNoCacheResourceTtlInSeconds);
+ return responseBuilder;
}
public HttpResponse removeResponse(HttpRequest request) {
@@ -89,7 +118,8 @@ public abstract class AbstractHttpCache
"GET".equals(request.getHeader("X-Method-Override")));
}
- protected boolean isCacheable(HttpRequest request, HttpResponse response) {
+ protected boolean isCacheable(HttpRequest request, HttpResponse response,
+ boolean allowStrictNoCacheResponses) {
if (!isCacheable(request)) {
return false;
}
@@ -104,8 +134,8 @@ public abstract class AbstractHttpCache
return false;
}
- // If the HTTP response allows for it, we can cache.
- return !response.isStrictNoCache();
+ // If we allow strict no-cache responses or the HTTP response allows for
it, we can cache.
+ return allowStrictNoCacheResponses || !response.isStrictNoCache();
}
/**
Modified:
shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/http/DefaultRequestPipeline.java
URL:
http://svn.apache.org/viewvc/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/http/DefaultRequestPipeline.java?rev=1068301&r1=1068300&r2=1068301&view=diff
==============================================================================
---
shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/http/DefaultRequestPipeline.java
(original)
+++
shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/http/DefaultRequestPipeline.java
Tue Feb 8 09:26:10 2011
@@ -80,8 +80,9 @@ public class DefaultRequestPipeline impl
if (!request.getIgnoreCache()) {
HttpResponse cachedResponse = httpCache.getResponse(request);
// Note that we don't remove invalidated entries from the cache as we
want them to be
- // available in the event of a backend fetch failure
- if (cachedResponse != null) {
+ // available in the event of a backend fetch failure.
+ // Note that strict no-cache entries are dummy responses and should not
be used.
+ if (cachedResponse != null && !cachedResponse.isStrictNoCache()) {
if (!cachedResponse.isStale()) {
if(invalidationService.isValid(request, cachedResponse)) {
return cachedResponse;
Modified:
shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/http/HttpResponseBuilder.java
URL:
http://svn.apache.org/viewvc/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/http/HttpResponseBuilder.java?rev=1068301&r1=1068300&r2=1068301&view=diff
==============================================================================
---
shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/http/HttpResponseBuilder.java
(original)
+++
shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/http/HttpResponseBuilder.java
Tue Feb 8 09:26:10 2011
@@ -34,7 +34,9 @@ import org.w3c.dom.Document;
import org.w3c.dom.DocumentFragment;
import java.nio.charset.Charset;
+import java.util.Arrays;
import java.util.Collection;
+import java.util.ListIterator;
import java.util.List;
import java.util.Map;
@@ -231,6 +233,22 @@ public class HttpResponseBuilder extends
return this;
}
+ public HttpResponseBuilder setCacheControlMaxAge(long expirationTime) {
+ String cacheControl = getHeader("Cache-Control");
+ List<String> directives = Lists.newArrayList();
+ if (cacheControl != null) {
+ for (String directive : StringUtils.split(cacheControl, ',')) {
+ directive = directive.trim();
+ if (!directive.startsWith("max-age") || StringUtils.split(directive,
'=').length != 2) {
+ directives.add(directive);
+ }
+ }
+ }
+ directives.add("max-age=" + expirationTime);
+ setHeader("Cache-Control", StringUtils.join(directives, ','));
+ return this;
+ }
+
/**
* Sets cache-control headers indicating the response is not cacheable.
*/
Modified:
shindig/trunk/java/gadgets/src/test/java/org/apache/shindig/gadgets/http/AbstractHttpCacheTest.java
URL:
http://svn.apache.org/viewvc/shindig/trunk/java/gadgets/src/test/java/org/apache/shindig/gadgets/http/AbstractHttpCacheTest.java?rev=1068301&r1=1068300&r2=1068301&view=diff
==============================================================================
---
shindig/trunk/java/gadgets/src/test/java/org/apache/shindig/gadgets/http/AbstractHttpCacheTest.java
(original)
+++
shindig/trunk/java/gadgets/src/test/java/org/apache/shindig/gadgets/http/AbstractHttpCacheTest.java
Tue Feb 8 09:26:10 2011
@@ -26,15 +26,24 @@ import static org.junit.Assert.assertTru
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.Maps;
+import com.google.inject.AbstractModule;
+import com.google.inject.Guice;
+import com.google.inject.Injector;
+import com.google.inject.Module;
+import com.google.inject.name.Names;
+import com.google.inject.util.Modules;
import org.apache.shindig.auth.BasicSecurityToken;
import org.apache.shindig.auth.SecurityToken;
import org.apache.shindig.common.uri.Uri;
+import org.apache.shindig.common.PropertiesModule;
import org.apache.shindig.gadgets.AuthType;
+import org.apache.shindig.gadgets.parse.ParseModule;
import org.apache.shindig.gadgets.oauth.OAuthArguments;
import org.apache.shindig.gadgets.spec.RequestAuthenticationInfo;
import org.apache.shindig.gadgets.uri.UriCommon.Param;
import org.easymock.EasyMock;
+import org.junit.Before;
import org.junit.Test;
import java.util.Map;
@@ -49,6 +58,22 @@ public class AbstractHttpCacheTest {
private static final String CONTAINER_NAME = "container";
private final TestHttpCache cache = new TestHttpCache();
+ // Cache designed to return 86400 for strictNoCacheResourceTtl.
+ private TestHttpCache extendedStrictNoCacheTtlCache;
+
+ @Before
+ public void setUp() {
+ Module module = new AbstractModule() {
+ @Override
+ public void configure() {
+ binder().bindConstant()
+
.annotatedWith(Names.named("shindig.cache.http.strict-no-cache-resource.max-age"))
+ .to(86400L);
+ }
+ };
+ Injector injector = Guice.createInjector(module);
+ extendedStrictNoCacheTtlCache = injector.getInstance(TestHttpCache.class);
+ }
@Test
public void createKeySimple() {
@@ -228,6 +253,9 @@ public class AbstractHttpCacheTest {
cache.map.put(key, response);
assertEquals(response, cache.getResponse(request));
+
+ extendedStrictNoCacheTtlCache.map.put(key, response);
+ assertEquals(response, extendedStrictNoCacheTtlCache.getResponse(request));
}
@Test
@@ -239,6 +267,10 @@ public class AbstractHttpCacheTest {
cache.map.put(key, response);
assertNull("Did not return null when method was POST",
cache.getResponse(request));
+
+ extendedStrictNoCacheTtlCache.map.put(key, response);
+ assertNull("Did not return null when method was POST",
+ extendedStrictNoCacheTtlCache.getResponse(request));
}
@Test
@@ -251,6 +283,9 @@ public class AbstractHttpCacheTest {
cache.map.put(key, response);
assertEquals(response, cache.getResponse(request));
+
+ extendedStrictNoCacheTtlCache.map.put(key, response);
+ assertEquals(response, extendedStrictNoCacheTtlCache.getResponse(request));
}
@Test
@@ -263,15 +298,23 @@ public class AbstractHttpCacheTest {
request.setIgnoreCache(true);
assertNull("Did not return null when ignoreCache was true",
cache.getResponse(request));
+
+ extendedStrictNoCacheTtlCache.map.put(key, response);
+ assertNull("Did not return null when ignoreCache was true",
+ extendedStrictNoCacheTtlCache.getResponse(request));
}
@Test
public void getResponseNotCacheable() {
HttpRequest request = new HttpRequest(DEFAULT_URI);
+ String key = cache.createKey(request);
HttpResponse response = new
HttpResponseBuilder().setStrictNoCache().create();
- cache.addResponse(request, response);
+ cache.map.put(key, response);
assertNull("Did not return null when response was uncacheable",
cache.getResponse(request));
+
+ extendedStrictNoCacheTtlCache.map.put(key, response);
+ assertEquals(response, extendedStrictNoCacheTtlCache.getResponse(request));
}
@Test
@@ -281,8 +324,11 @@ public class AbstractHttpCacheTest {
String key = cache.createKey(request);
assertTrue("response should have been cached", cache.addResponse(request,
response));
-
assertEquals(response, cache.map.get(key));
+
+ assertTrue("response should have been cached",
+ extendedStrictNoCacheTtlCache.addResponse(request, response));
+ assertEquals(response, extendedStrictNoCacheTtlCache.map.get(key));
}
@Test
@@ -292,26 +338,39 @@ public class AbstractHttpCacheTest {
HttpResponse response = new HttpResponse("does not matter");
assertFalse("response should not have been cached",
cache.addResponse(request, response));
-
assertEquals(0, cache.map.size());
+
+ assertFalse("response should not have been cached",
+ extendedStrictNoCacheTtlCache.addResponse(request, response));
+ assertEquals(0, extendedStrictNoCacheTtlCache.map.size());
}
@Test
public void addResponseNotCacheable() {
HttpRequest request = new HttpRequest(DEFAULT_URI);
HttpResponse response = new
HttpResponseBuilder().setStrictNoCache().create();
- assertFalse(cache.addResponse(request, response));
+ String key = cache.createKey(request);
+ assertFalse(cache.addResponse(request, response));
assertEquals(0, cache.map.size());
+
+ assertTrue("response should have been cached",
+ extendedStrictNoCacheTtlCache.addResponse(request, response));
+ assertEquals(
+ extendedStrictNoCacheTtlCache.buildStrictNoCacheHttpResponse(request,
response).create(),
+ extendedStrictNoCacheTtlCache.map.get(key));
}
@Test
public void addResponseIfModifiedSince() {
HttpRequest request = new HttpRequest(DEFAULT_URI);
HttpResponse response = new
HttpResponseBuilder().setHttpStatusCode(HttpResponse.SC_NOT_MODIFIED).create();
- assertFalse(cache.addResponse(request, response));
+ assertFalse(cache.addResponse(request, response));
assertEquals(0, cache.map.size());
+
+ assertFalse(extendedStrictNoCacheTtlCache.addResponse(request, response));
+ assertEquals(0, extendedStrictNoCacheTtlCache.map.size());
}
@Test
@@ -319,9 +378,12 @@ public class AbstractHttpCacheTest {
HttpRequest request = new HttpRequest(DEFAULT_URI)
.setMethod("POST");
HttpResponse response = new HttpResponse("does not matter");
- assertFalse(cache.addResponse(request, response));
+ assertFalse(cache.addResponse(request, response));
assertEquals(0, cache.map.size());
+
+ assertFalse(extendedStrictNoCacheTtlCache.addResponse(request, response));
+ assertEquals(0, extendedStrictNoCacheTtlCache.map.size());
}
@Test
@@ -333,8 +395,10 @@ public class AbstractHttpCacheTest {
String key = cache.createKey(request);
assertTrue(cache.addResponse(request, response));
-
assertEquals(response, cache.map.get(key));
+
+ assertTrue(extendedStrictNoCacheTtlCache.addResponse(request, response));
+ assertEquals(response, extendedStrictNoCacheTtlCache.map.get(key));
}
@Test
@@ -348,6 +412,10 @@ public class AbstractHttpCacheTest {
assertTrue(cache.addResponse(request, response));
assertEquals("public,max-age=10",
cache.map.get(key).getHeader("Cache-Control"));
+
+ assertTrue(extendedStrictNoCacheTtlCache.addResponse(request, response));
+ assertEquals("public,max-age=10",
+
extendedStrictNoCacheTtlCache.map.get(key).getHeader("Cache-Control"));
}
@Test
@@ -364,6 +432,10 @@ public class AbstractHttpCacheTest {
assertTrue(cache.addResponse(request, response));
assertEquals("public,max-age=10",
cache.map.get(key).getHeader("Cache-Control"));
+
+ assertTrue(extendedStrictNoCacheTtlCache.addResponse(request, response));
+ assertEquals("public,max-age=10",
+
extendedStrictNoCacheTtlCache.map.get(key).getHeader("Cache-Control"));
}
@Test
@@ -376,6 +448,42 @@ public class AbstractHttpCacheTest {
assertTrue(cache.addResponse(request, response));
assertEquals("no headers", cache.map.get(key).getResponseAsString());
+
+ assertTrue(extendedStrictNoCacheTtlCache.addResponse(request, response));
+ assertEquals("no headers",
extendedStrictNoCacheTtlCache.map.get(key).getResponseAsString());
+ }
+
+ @Test
+ public void buildStrictNoCacheHttpResponse() {
+ HttpResponse response = new HttpResponseBuilder()
+ .setResponseString("result")
+ .addHeader("Cache-Control", "private")
+ .addHeader("X-Method-Override", "GET")
+ .create();
+ assertTrue(response.isStrictNoCache());
+ HttpResponse builtResponse = extendedStrictNoCacheTtlCache
+ .buildStrictNoCacheHttpResponse(new HttpRequest(DEFAULT_URI),
response).create();
+
+ assertTrue(builtResponse.isStrictNoCache());
+ assertEquals("", builtResponse.getResponseAsString());
+ assertEquals("private,max-age=86400",
builtResponse.getHeader("Cache-Control"));
+ assertNull(builtResponse.getHeader("X-Method-Override"));
+ }
+
+ @Test
+ public void buildStrictNoCacheHttpResponseWithPragmaHeader() {
+ HttpResponse response = new HttpResponseBuilder()
+ .setResponseString("result")
+ .addHeader("Pragma", "no-cache")
+ .create();
+ assertTrue(response.isStrictNoCache());
+ HttpResponse builtResponse = cache
+ .buildStrictNoCacheHttpResponse(new HttpRequest(DEFAULT_URI),
response).create();
+
+ assertTrue(builtResponse.isStrictNoCache());
+ assertEquals("", builtResponse.getResponseAsString());
+ assertEquals("max-age=-1", builtResponse.getHeader("Cache-Control"));
+ assertEquals("no-cache", builtResponse.getHeader("Pragma"));
}
@Test
Modified:
shindig/trunk/java/gadgets/src/test/java/org/apache/shindig/gadgets/http/HttpResponseBuilderTest.java
URL:
http://svn.apache.org/viewvc/shindig/trunk/java/gadgets/src/test/java/org/apache/shindig/gadgets/http/HttpResponseBuilderTest.java?rev=1068301&r1=1068300&r2=1068301&view=diff
==============================================================================
---
shindig/trunk/java/gadgets/src/test/java/org/apache/shindig/gadgets/http/HttpResponseBuilderTest.java
(original)
+++
shindig/trunk/java/gadgets/src/test/java/org/apache/shindig/gadgets/http/HttpResponseBuilderTest.java
Tue Feb 8 09:26:10 2011
@@ -106,6 +106,57 @@ public class HttpResponseBuilderTest {
}
@Test
+ public void setCacheControlMaxAge() {
+ HttpResponseBuilder builder = new HttpResponseBuilder()
+ .addHeader("Cache-Control", "public,max-age=100")
+ .setCacheControlMaxAge(12345);
+
+ Multimap<String, String> headers = builder.getHeaders();
+ assertEquals("public,max-age=12345",
headers.get("Cache-Control").iterator().next());
+ }
+
+ @Test
+ public void setCacheControlMaxAgeWithSpacesInCacheControlHeader() {
+ HttpResponseBuilder builder = new HttpResponseBuilder()
+ .addHeader("Cache-Control", "public, max-age=123, no-transform ")
+ .setCacheControlMaxAge(12345);
+
+ Multimap<String, String> headers = builder.getHeaders();
+ assertEquals("public,no-transform,max-age=12345",
+ headers.get("Cache-Control").iterator().next());
+ }
+
+ @Test
+ public void setCacheControlMaxAgeWithBadMaxAgeFormat() {
+ HttpResponseBuilder builder = new HttpResponseBuilder()
+ .addHeader("Cache-Control", "public, max-age=12=ab")
+ .setCacheControlMaxAge(12345);
+
+ Multimap<String, String> headers = builder.getHeaders();
+ assertEquals("public,max-age=12=ab,max-age=12345",
+ headers.get("Cache-Control").iterator().next());
+ }
+
+ @Test
+ public void setCacheControlMaxAgeWithNoInitialMaxAge() {
+ HttpResponseBuilder builder = new HttpResponseBuilder()
+ .addHeader("Cache-Control", "private")
+ .setCacheControlMaxAge(10000);
+
+ Multimap<String, String> headers = builder.getHeaders();
+ assertEquals("private,max-age=10000",
headers.get("Cache-Control").iterator().next());
+ }
+
+ @Test
+ public void setCacheControlMaxAgeWithNoCacheControlHeader() {
+ HttpResponseBuilder builder = new HttpResponseBuilder()
+ .setCacheControlMaxAge(86400);
+
+ Multimap<String, String> headers = builder.getHeaders();
+ assertEquals("max-age=86400",
headers.get("Cache-Control").iterator().next());
+ }
+
+ @Test
public void setCacheTtl() {
HttpResponseBuilder builder = new HttpResponseBuilder()
.addHeader("Pragma", "no-cache")