You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@shindig.apache.org by zh...@apache.org on 2010/10/08 01:19:52 UTC

svn commit: r1005661 - in /shindig/trunk/java/gadgets/src: main/java/org/apache/shindig/gadgets/rewrite/ main/java/org/apache/shindig/gadgets/servlet/ main/java/org/apache/shindig/gadgets/uri/ test/java/org/apache/shindig/gadgets/rewrite/ test/java/org...

Author: zhoresh
Date: Thu Oct  7 23:19:52 2010
New Revision: 1005661

URL: http://svn.apache.org/viewvc?rev=1005661&view=rev
Log:
Ref: http://codereview.appspot.com/2298042/
Proxy should use resource TTL by default

Modified:
    shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/rewrite/ContentRewriterFeature.java
    shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/servlet/ProxyHandler.java
    shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/uri/ProxyUriBase.java
    shindig/trunk/java/gadgets/src/test/java/org/apache/shindig/gadgets/rewrite/ContentRewriterFeatureTestCase.java
    shindig/trunk/java/gadgets/src/test/java/org/apache/shindig/gadgets/servlet/ProxyHandlerTest.java

Modified: shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/rewrite/ContentRewriterFeature.java
URL: http://svn.apache.org/viewvc/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/rewrite/ContentRewriterFeature.java?rev=1005661&r1=1005660&r2=1005661&view=diff
==============================================================================
--- shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/rewrite/ContentRewriterFeature.java (original)
+++ shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/rewrite/ContentRewriterFeature.java Thu Oct  7 23:19:52 2010
@@ -55,13 +55,13 @@ public class ContentRewriterFeature {
   protected static final String EXCLUDE_URL = "exclude-url";
   protected static final String INCLUDE_TAGS = "include-tags";
   protected static final String EXPIRES = "expires";
-  
-  public static final Integer EXPIRES_DEFAULT = -1;  // -1 = Use HTTP.
+
+  public static final Integer EXPIRES_HTTP = -1;  // -1 = Use HTTP.
 
   protected enum PatternOptions {
     ALL, NONE, REGEX, STRINGS
   }
-  
+
   /**
    * Factory for content rewriter features.
    */
@@ -79,7 +79,7 @@ public class ContentRewriterFeature {
     public Config getDefault() {
       return defaultConfig;
     }
-    
+
     public Config get(HttpRequest request) {
       return get(request.getGadget());
     }
@@ -124,7 +124,7 @@ public class ContentRewriterFeature {
           "", false, true);
     }
   }
-  
+
   @Singleton
   public static class DefaultConfig extends Config {
     @Inject
@@ -138,7 +138,7 @@ public class ContentRewriterFeature {
       super(includeUrls, excludeUrls, expires, includeTags, onlyAllowExcludes, enableSplitJsConcat);
     }
   }
-  
+
   public static class Config {
     private final MatchBundle includes;
     private final MatchBundle excludes;
@@ -151,7 +151,7 @@ public class ContentRewriterFeature {
     private final Integer expires;
     private final boolean onlyAllowExcludes;
     private final boolean enableSplitJs;
-    
+
     // Lazily computed
     private Integer fingerprint;
     private static final Pattern COMMA_WHITESPACE_PATTERN = Pattern.compile("\\s*,\\s*");
@@ -159,7 +159,7 @@ public class ContentRewriterFeature {
     /**
      * Constructor which takes a gadget spec and container settings
      * as "raw" input strings.
-     * 
+     *
      * @param defaultInclude As a regex
      * @param defaultExclude As a regex
      * @param defaultExpires Either "HTTP" or a ttl in seconds
@@ -173,7 +173,7 @@ public class ContentRewriterFeature {
       // Set up includes from defaultInclude param
       this.includes = getMatchBundle(paramTrim(defaultInclude),
           Collections.<String>emptyList());
-      
+
       // Set up excludes from defaultExclude param
       this.excludes = getMatchBundle(paramTrim(defaultExclude),
           Collections.<String>emptyList());
@@ -186,26 +186,26 @@ public class ContentRewriterFeature {
         }
       }
       this.includeTags = includeTagsBuilder.build();
-      
+
       // Parse expires field
-      int expiresVal = EXPIRES_DEFAULT;
+      int expiresVal = EXPIRES_HTTP;
       try {
         expiresVal = Integer.parseInt(paramTrim(defaultExpires));
       } catch (NumberFormatException e) {
         // Fall through to default.
       }
       this.expires = expiresVal;
-      
+
       // Save config for onlyAllowExcludes
       this.onlyAllowExcludes = onlyAllowExcludes;
       this.enableSplitJs = enableSplitJs;
     }
-    
+
     Config(GadgetSpec spec, Config defaultConfig) {
       this.onlyAllowExcludes = defaultConfig.onlyAllowExcludes;
-      
+
       Feature f = spec.getModulePrefs().getFeatures().get("content-rewrite");
-      
+
       // Include overrides.
       // Note: Shindig originally supported the plural versions with regular
       // expressions. But the OpenSocial specification v0.9 allows for singular
@@ -224,7 +224,7 @@ public class ContentRewriterFeature {
         }
       }
       this.includes = getMatchBundle(includeRegex, includeUrls);
-      
+
       // Exclude overrides. Only use the exclude regex specified by the
       // gadget spec if !onlyAllowExcludes.
       String excludeRegex = defaultConfig.excludes.param;
@@ -239,7 +239,7 @@ public class ContentRewriterFeature {
         }
       }
       this.excludes = getMatchBundle(excludeRegex, excludeUrls);
-      
+
       // Spec-specified include tags.
       Set<String> tagsVal = null;
       if (f != null && f.getParams().containsKey(INCLUDE_TAGS)) {
@@ -264,31 +264,32 @@ public class ContentRewriterFeature {
       if (f != null && f.getParams().containsKey(EXPIRES)) {
         try {
           int overrideVal = Integer.parseInt(f.getParam(EXPIRES));
-          expiresVal = Math.min(overrideVal, expiresVal);
+          expiresVal = (expiresVal == EXPIRES_HTTP || overrideVal < expiresVal) ?
+              overrideVal : expiresVal;
         } catch (NumberFormatException e) {
           // Falls through to default.
           if ("HTTP".equalsIgnoreCase(f.getParam(EXPIRES).trim())) {
-            expiresVal = EXPIRES_DEFAULT;
+            expiresVal = EXPIRES_HTTP;
           }
         }
       }
       this.expires = expiresVal;
       this.enableSplitJs = defaultConfig.enableSplitJs;
     }
-    
+
     private String paramTrim(String param) {
       if (param == null) {
         return param;
       }
-      
+
       return param.trim();
     }
-    
+
     private MatchBundle getMatchBundle(String regex, Collection<String> matches) {
       MatchBundle bundle = new MatchBundle();
       bundle.param = regex;
       bundle.matches = matches;
-      
+
       if (bundle.matches.isEmpty() && StringUtils.isEmpty(bundle.param)) {
         bundle.options = PatternOptions.NONE;
       } else if (bundle.matches.size() == 1) {
@@ -312,7 +313,7 @@ public class ContentRewriterFeature {
       }
       return bundle;
     }
-    
+
     private static class MatchBundle {
       private String param;
       private PatternOptions options;
@@ -327,7 +328,7 @@ public class ContentRewriterFeature {
     protected boolean shouldExclude(String url) {
       return matcherMatches(url, excludes);
     }
-    
+
     private static boolean matcherMatches(String url, MatchBundle bundle) {
       switch (bundle.options) {
       case NONE:
@@ -374,7 +375,7 @@ public class ContentRewriterFeature {
     public Integer getExpires() {
       return expires;
     }
-    
+
     public boolean isSplitJsEnabled() {
       return enableSplitJs;
     }

Modified: shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/servlet/ProxyHandler.java
URL: http://svn.apache.org/viewvc/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/servlet/ProxyHandler.java?rev=1005661&r1=1005660&r2=1005661&view=diff
==============================================================================
--- shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/servlet/ProxyHandler.java (original)
+++ shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/servlet/ProxyHandler.java Thu Oct  7 23:19:52 2010
@@ -45,7 +45,6 @@ import java.io.IOException;
 public class ProxyHandler {
   // TODO: parameterize these.
   static final Integer LONG_LIVED_REFRESH = (365 * 24 * 60 * 60);  // 1 year
-  static final Integer DEFAULT_REFRESH = (60 * 60);                // 1 hour
 
   private final RequestPipeline requestPipeline;
   private final ResponseRewriterRegistry contentRewriterRegistry;
@@ -109,7 +108,8 @@ public class ProxyHandler {
 
     try {
       ServletUtil.setCachingHeaders(response,
-          proxyUri.translateStatusRefresh(LONG_LIVED_REFRESH, DEFAULT_REFRESH), false);
+          proxyUri.translateStatusRefresh(LONG_LIVED_REFRESH, (int) (results.getCacheTtl() / 1000)),
+          false);
     } catch (GadgetException gex) {
       return ServletUtil.errorResponse(gex);
     }

Modified: shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/uri/ProxyUriBase.java
URL: http://svn.apache.org/viewvc/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/uri/ProxyUriBase.java?rev=1005661&r1=1005660&r2=1005661&view=diff
==============================================================================
--- shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/uri/ProxyUriBase.java (original)
+++ shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/uri/ProxyUriBase.java Thu Oct  7 23:19:52 2010
@@ -45,7 +45,7 @@ public class ProxyUriBase {
   private String rewriteMimeType = null;
   private boolean sanitizeContent = false;
   private boolean cajoleContent = false;
-  
+
   protected ProxyUriBase(Gadget gadget) {
     this(null,  // Meaningless in "context" mode. translateStatusRefresh invalid here.
          getIntegerValue(gadget.getContext().getParameter(Param.REFRESH.getKey())),
@@ -54,7 +54,7 @@ public class ProxyUriBase {
          gadget.getContext().getContainer(),
          gadget.getSpec().getUrl().toString());
   }
-  
+
   protected ProxyUriBase(UriStatus status, Uri origUri) {
     this.status = status;
     setFromUri(origUri);
@@ -89,8 +89,8 @@ public class ProxyUriBase {
       gadget = uri.getQueryParameter(Param.GADGET.getKey());
       rewriteMimeType = uri.getQueryParameter(Param.REWRITE_MIME_TYPE.getKey());
       sanitizeContent = getBooleanValue(uri.getQueryParameter(Param.SANITIZE.getKey()));
-      cajoleContent = getBooleanValue(uri.getQueryParameter(Param.CAJOLE.getKey()));      
-    }  
+      cajoleContent = getBooleanValue(uri.getQueryParameter(Param.CAJOLE.getKey()));
+    }
   }
 
   @Override
@@ -99,7 +99,7 @@ public class ProxyUriBase {
       return true;
     }
     if (!(obj instanceof ProxyUriBase)) {
-      return false; 
+      return false;
     }
     ProxyUriBase objUri = (ProxyUriBase) obj;
     return (Objects.equal(this.status, objUri.status)
@@ -124,7 +124,7 @@ public class ProxyUriBase {
     this.rewriteMimeType = type;
     return this;
   }
-  
+
   public ProxyUriBase setSanitizeContent(boolean sanitize) {
     this.sanitizeContent = sanitize;
     return this;
@@ -134,7 +134,7 @@ public class ProxyUriBase {
     this.cajoleContent = cajole;
     return this;
   }
-  
+
   public UriStatus getStatus() {
     return status;
   }
@@ -162,7 +162,7 @@ public class ProxyUriBase {
   public String getRewriteMimeType() {
     return rewriteMimeType;
   }
-  
+
   public boolean sanitizeContent() {
     return sanitizeContent;
   }
@@ -195,20 +195,20 @@ public class ProxyUriBase {
       req.setRewriteMimeType(getRewriteMimeType());
     }
     req.setSanitizationRequested(sanitizeContent());
-    req.setCajaRequested(cajoleContent());    
+    req.setCajaRequested(cajoleContent());
 
     return req;
   }
 
   /**
-   * Construct the query parameters for proxy url  
-   * @param forcedRefresh optional overwrite the refresh time 
+   * Construct the query parameters for proxy url
+   * @param forcedRefresh optional overwrite the refresh time
    * @param version optional version
    * @return Url with only query parameters set
    */
   public UriBuilder makeQueryParams(Integer forcedRefresh, String version) {
     UriBuilder queryBuilder = new UriBuilder();
-    
+
     // Add all params common to both chained and query syntax.
     String container = getContainer();
     queryBuilder.addQueryParameter(Param.CONTAINER.getKey(), container);
@@ -219,7 +219,7 @@ public class ProxyUriBase {
       if (forcedRefresh != null && forcedRefresh >= 0) {
         queryBuilder.addQueryParameter(Param.REFRESH.getKey(), forcedRefresh.toString());
       } else if (getRefresh() != null) {
-        queryBuilder.addQueryParameter(Param.REFRESH.getKey(), getRefresh().toString());      
+        queryBuilder.addQueryParameter(Param.REFRESH.getKey(), getRefresh().toString());
       }
     }
 
@@ -238,7 +238,19 @@ public class ProxyUriBase {
     return queryBuilder;
   }
 
-  public Integer translateStatusRefresh(int longVal, int defaultVal)
+  /**
+   * Calculate cache time for a resource url. Provide long period for versioned resource,
+   * use original value for unversioned resource, and no cache for invalid versions.
+   * Invalid version can happen in multiple instances environemnt where the url can be
+   * created on one server and the actually retreival is done on another that have an older version
+   * of resource in cache. In that case no caching will cause the user browser to try again next
+   * time and hopefully getting the newer version.
+   * @param longVal long expiry for versioned resource
+   * @param originalResourceTtl original resource ttl, to be served fro unversion resource
+   * @return the calculated expiry to the Uri according to status
+   * @throws GadgetException
+   */
+  public Integer translateStatusRefresh(int longVal, int originalResourceTtl)
       throws GadgetException {
     Integer retRefresh = 0;
     switch (getStatus()) {
@@ -246,7 +258,7 @@ public class ProxyUriBase {
       retRefresh = longVal;
       break;
     case VALID_UNVERSIONED:
-      retRefresh = defaultVal;
+      retRefresh = originalResourceTtl;
       break;
     case INVALID_VERSION:
       retRefresh = 0;
@@ -275,7 +287,7 @@ public class ProxyUriBase {
   protected static boolean getBooleanValue(String str) {
     return str != null && "1".equals(str);
   }
-  
+
   protected static Integer getIntegerValue(String str) {
     Integer val = null;
     try {

Modified: shindig/trunk/java/gadgets/src/test/java/org/apache/shindig/gadgets/rewrite/ContentRewriterFeatureTestCase.java
URL: http://svn.apache.org/viewvc/shindig/trunk/java/gadgets/src/test/java/org/apache/shindig/gadgets/rewrite/ContentRewriterFeatureTestCase.java?rev=1005661&r1=1005660&r2=1005661&view=diff
==============================================================================
--- shindig/trunk/java/gadgets/src/test/java/org/apache/shindig/gadgets/rewrite/ContentRewriterFeatureTestCase.java (original)
+++ shindig/trunk/java/gadgets/src/test/java/org/apache/shindig/gadgets/rewrite/ContentRewriterFeatureTestCase.java Thu Oct  7 23:19:52 2010
@@ -136,7 +136,39 @@ public class ContentRewriterFeatureTestC
     defaultRewriterFeature = new ContentRewriterFeature.Config(
         createSpecWithRewrite("test\\.com", "testx", "htTp ", tags),
         new ContentRewriterFeature.DefaultConfig("", "", "12345", TAGS, false, false));
-    assertEquals(ContentRewriterFeature.EXPIRES_DEFAULT, defaultRewriterFeature.getExpires());
+    assertEquals(ContentRewriterFeature.EXPIRES_HTTP, defaultRewriterFeature.getExpires());
+  }
+
+  @Test
+  public void testExpiresOverwriteTooBig() throws Exception {
+    defaultRewriterFeature = new ContentRewriterFeature.Config(
+        createSpecWithRewrite("test\\.com", "testx", "20000", tags),
+        new ContentRewriterFeature.DefaultConfig("", "", "12345", TAGS, false, false));
+    assertEquals(12345, defaultRewriterFeature.getExpires().intValue());
+  }
+
+  @Test
+  public void testExpiresBadValue() throws Exception {
+    defaultRewriterFeature = new ContentRewriterFeature.Config(
+        createSpecWithRewrite("test\\.com", "testx", "X", tags),
+        new ContentRewriterFeature.DefaultConfig("", "", "12345", TAGS, false, false));
+    assertEquals(12345, defaultRewriterFeature.getExpires().intValue());
+  }
+
+  @Test
+  public void testExpiresOverwrite() throws Exception {
+    defaultRewriterFeature = new ContentRewriterFeature.Config(
+        createSpecWithRewrite("test\\.com", "testx", "10", tags),
+        new ContentRewriterFeature.DefaultConfig("", "", "12345", TAGS, false, false));
+    assertEquals(10, defaultRewriterFeature.getExpires().intValue());
+  }
+
+  @Test
+  public void testExpiresOverwriteDefault() throws Exception {
+    defaultRewriterFeature = new ContentRewriterFeature.Config(
+        createSpecWithRewrite("test\\.com", "testx", "10", tags),
+        new ContentRewriterFeature.DefaultConfig("", "", "-1", TAGS, false, false));
+    assertEquals(10, defaultRewriterFeature.getExpires().intValue());
   }
 
   @Test
@@ -187,14 +219,14 @@ public class ContentRewriterFeatureTestC
     assertTrue(defaultRewriterFeature.shouldRewriteURL("http://www.foobar.com"));
     assertFalse(defaultRewriterFeature.shouldRewriteURL("http://www.test.com"));
   }
-  
+
   @Test
   public void testSplitJsSupported() throws Exception {
     defaultRewriterFeature =
         new ContentRewriterFeature.DefaultConfig("", "test", "0", TAGS, false, true);
     assertTrue(defaultRewriterFeature.isSplitJsEnabled());
   }
-  
+
   @Test
   public void testSplitJsNotSupported() throws Exception {
     defaultRewriterFeature =

Modified: shindig/trunk/java/gadgets/src/test/java/org/apache/shindig/gadgets/servlet/ProxyHandlerTest.java
URL: http://svn.apache.org/viewvc/shindig/trunk/java/gadgets/src/test/java/org/apache/shindig/gadgets/servlet/ProxyHandlerTest.java?rev=1005661&r1=1005660&r2=1005661&view=diff
==============================================================================
--- shindig/trunk/java/gadgets/src/test/java/org/apache/shindig/gadgets/servlet/ProxyHandlerTest.java (original)
+++ shindig/trunk/java/gadgets/src/test/java/org/apache/shindig/gadgets/servlet/ProxyHandlerTest.java Thu Oct  7 23:19:52 2010
@@ -23,6 +23,7 @@ import com.google.common.collect.Maps;
 
 import org.apache.shindig.common.EasyMockTestCase;
 import org.apache.shindig.common.uri.Uri;
+import org.apache.shindig.common.util.FakeTimeSource;
 import org.apache.shindig.config.ContainerConfig;
 import org.apache.shindig.gadgets.Gadget;
 import org.apache.shindig.gadgets.GadgetException;
@@ -59,10 +60,10 @@ public class ProxyHandlerTest extends Ea
   public ResponseRewriterRegistry rewriterRegistry
       = new DefaultResponseRewriterRegistry(Arrays.<ResponseRewriter>asList(rewriter), null);
   private ProxyUriManager.ProxyUri request;
-  
+
   private final ProxyHandler proxyHandler
       = new ProxyHandler(pipeline, rewriterRegistry, true);
-  
+
   private void expectGetAndReturnData(String url, byte[] data) throws Exception {
     HttpRequest req = new HttpRequest(Uri.parse(url));
     HttpResponse resp = new HttpResponseBuilder().setResponse(data).create();
@@ -75,7 +76,7 @@ public class ProxyHandlerTest extends Ea
     HttpResponse resp = new HttpResponseBuilder().addAllHeaders(headers).create();
     expect(pipeline.execute(req)).andReturn(resp);
   }
-  
+
   private void setupProxyRequestMock(String host, String url,
       boolean noCache, int refresh, String rewriteMime, String fallbackUrl) throws Exception {
     request = new ProxyUriManager.ProxyUri(
@@ -93,12 +94,14 @@ public class ProxyHandlerTest extends Ea
   private ResponseRewriter getResponseRewriterThatThrowsExceptions(
       final StringBuilder stringBuilder) {
     return new DomWalker.Rewriter() {
+      @Override
       public void rewrite(Gadget gadget, MutableContent content)
           throws RewritingException {
         stringBuilder.append("exceptionThrown");
         throw new RewritingException("sad", 404);
       }
 
+      @Override
       public void rewrite(HttpRequest request, HttpResponseBuilder builder)
           throws RewritingException {
         stringBuilder.append("exceptionThrown");
@@ -140,7 +143,7 @@ public class ProxyHandlerTest extends Ea
   public void testLockedDomainEmbed() throws Exception {
     setupNoArgsProxyRequestMock("www.example.com", URL_ONE);
     expectGetAndReturnData(URL_ONE, DATA_ONE.getBytes());
-   
+
     replay();
     HttpResponse response = proxyHandler.fetch(request);
     verify();
@@ -201,7 +204,7 @@ public class ProxyHandlerTest extends Ea
     assertEquals(magicGarbage, response.getHeader("X-Magic-Garbage"));
     assertTrue(rewriter.responseWasRewritten());
   }
-  
+
   @Test
   public void testOctetSetOnNullContentType() throws Exception {
     String url = "http://example.org/file.evil";
@@ -218,7 +221,7 @@ public class ProxyHandlerTest extends Ea
     assertNotNull(response.getHeader("Content-Disposition"));
     assertTrue(rewriter.responseWasRewritten());
   }
-  
+
   @Test
   public void testNoContentDispositionForFlash() throws Exception {
     // Some headers may be blacklisted. These are OK.
@@ -238,7 +241,7 @@ public class ProxyHandlerTest extends Ea
     assertNull(response.getHeader("Content-Disposition"));
     assertTrue(rewriter.responseWasRewritten());
   }
-  
+
   @Test
   public void testGetFallback() throws Exception {
     String url = "http://example.org/file.evil";
@@ -379,15 +382,18 @@ public class ProxyHandlerTest extends Ea
   public void testWithCache() throws Exception {
     String url = "http://example.org/file.evil";
     String domain = "example.org";
+    HttpResponse.setTimeSource(new FakeTimeSource());
 
     setupProxyRequestMock(domain, url, false, 120, null, null);
-    
+
     HttpRequest req = new HttpRequestCache(Uri.parse(url)).setCacheTtl(120).setIgnoreCache(false);
-    HttpResponse resp = new HttpResponse("Hello");
-    expect(pipeline.execute(req)).andReturn(resp);
+    HttpResponseBuilder resp = new HttpResponseBuilder().setCacheTtl(1234);
+    resp.setContent("Hello");
+    expect(pipeline.execute(req)).andReturn(resp.create());
 
     replay();
-    proxyHandler.fetch(request);
+    HttpResponse proxyResp = proxyHandler.fetch(request);
+    assertEquals(120, proxyResp.getCacheTtl() / 1000);
     verify();
   }
 
@@ -395,15 +401,18 @@ public class ProxyHandlerTest extends Ea
   public void testWithBadTtl() throws Exception {
     String url = "http://example.org/file.evil";
     String domain = "example.org";
+    HttpResponse.setTimeSource(new FakeTimeSource());
 
     setupProxyRequestMock(domain, url, false, -1, null, null);
-    
+
     HttpRequest req = new HttpRequestCache(Uri.parse(url)).setCacheTtl(-1).setIgnoreCache(false);
-    HttpResponse resp = new HttpResponse("Hello");
-    expect(pipeline.execute(req)).andReturn(resp);
+    HttpResponseBuilder resp = new HttpResponseBuilder().setCacheTtl(1234);
+    resp.setContent("Hello");
+    expect(pipeline.execute(req)).andReturn(resp.create());
 
     replay();
-    proxyHandler.fetch(request);
+    HttpResponse proxyResp = proxyHandler.fetch(request);
+    assertEquals(1234, proxyResp.getCacheTtl() / 1000);
     verify();
   }
 
@@ -414,7 +423,7 @@ public class ProxyHandlerTest extends Ea
     String domain = "example.org";
 
     setupProxyRequestMock(domain, url, false, -1, expectedMime, null);
-    
+
     HttpRequest req = new HttpRequest(Uri.parse(url))
         .setRewriteMimeType(expectedMime);
 
@@ -424,11 +433,11 @@ public class ProxyHandlerTest extends Ea
       .create();
 
     expect(pipeline.execute(req)).andReturn(resp);
-    
+
     replay();
     HttpResponse response = proxyHandler.fetch(request);
     verify();
-    
+
     assertEquals(outputMime, response.getHeader("Content-Type"));
     reset();
   }