You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@shindig.apache.org by jo...@apache.org on 2010/08/18 18:53:03 UTC

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

Author: johnh
Date: Wed Aug 18 16:53:02 2010
New Revision: 986785

URL: http://svn.apache.org/viewvc?rev=986785&view=rev
Log:
When parameter &rooe=[1,true], Return Original On Error in the proxy.

This applies when a rewriting pass throws an exception, and the proxy user has indicated that returning original content is acceptable.

Patch provided by Gagan Singh.


Modified:
    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/ProxyUriManager.java
    shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/uri/UriCommon.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/servlet/ProxyHandler.java
URL: http://svn.apache.org/viewvc/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/servlet/ProxyHandler.java?rev=986785&r1=986784&r2=986785&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 Wed Aug 18 16:53:02 2010
@@ -32,6 +32,7 @@ import org.apache.shindig.gadgets.http.R
 import org.apache.shindig.gadgets.rewrite.ResponseRewriterRegistry;
 import org.apache.shindig.gadgets.rewrite.RewritingException;
 import org.apache.shindig.gadgets.uri.ProxyUriManager;
+import org.apache.shindig.gadgets.uri.UriCommon;
 import org.apache.shindig.gadgets.uri.UriUtils;
 import org.apache.shindig.gadgets.uri.UriUtils.DisallowedHeaders;
 
@@ -46,7 +47,7 @@ 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;
 
@@ -73,11 +74,11 @@ public class ProxyHandler {
     HttpRequest rcr = buildHttpRequest(proxyUri, proxyUri.getResource());
     if (rcr == null) {
       throw new GadgetException(GadgetException.Code.INVALID_PARAMETER,
-          "No url parameter in request", HttpResponse.SC_BAD_REQUEST);      
+          "No url parameter in request", HttpResponse.SC_BAD_REQUEST);
     }
-    
+
     HttpResponse results = requestPipeline.execute(rcr);
-    
+
     if (results.isError()) {
       // Error: try the fallback. Particularly useful for proxied images.
       Uri fallbackUri = proxyUri.getFallbackUri();
@@ -86,44 +87,48 @@ public class ProxyHandler {
         results = requestPipeline.execute(fallbackRcr);
       }
     }
-    
+
     if (contentRewriterRegistry != null) {
       try {
         results = contentRewriterRegistry.rewriteHttpResponse(rcr, results);
       } catch (RewritingException e) {
-        throw new GadgetException(GadgetException.Code.INTERNAL_SERVER_ERROR, e,
-            e.getHttpStatusCode());
+        // Throw exception if the RETURN_ORIGINAL_CONTENT_ON_ERROR param is not
+        // set to "true" or the error is irrecoverable from.
+        if (!proxyUri.shouldReturnOrigOnErr() || !isRecoverable(results)) {
+          throw new GadgetException(GadgetException.Code.INTERNAL_SERVER_ERROR, e,
+              e.getHttpStatusCode());
+        }
       }
     }
-    
+
     HttpResponseBuilder response = new HttpResponseBuilder(results);
-    
+
     try {
       ServletUtil.setCachingHeaders(response,
           proxyUri.translateStatusRefresh(LONG_LIVED_REFRESH, DEFAULT_REFRESH), false);
     } catch (GadgetException gex) {
       return ServletUtil.errorResponse(gex);
     }
-    
+
     UriUtils.copyResponseHeadersAndStatusCode(results, response, true, true,
         DisallowedHeaders.CACHING_DIRECTIVES,  // Proxy sets its own caching headers.
         DisallowedHeaders.CLIENT_STATE_DIRECTIVES,  // Overridden or irrelevant to proxy.
         DisallowedHeaders.OUTPUT_TRANSFER_DIRECTIVES
         );
-    
+
     // Set Content-Type and Content-Disposition. Do this after copy results headers,
     // in order to prevent those from overwriting the correct values.
     setResponseContentHeaders(response, results);
-    
+
     response.setHeader("Content-Type", getContentType(rcr, response));
-    
+
     // TODO: replace this with streaming APIs when ready
     ByteArrayOutputStream baos = new ByteArrayOutputStream();
     IOUtils.copy(results.getResponse(), baos);
     response.setResponse(baos.toByteArray());
     return response.create();
   }
-  
+
   private String getContentType(HttpRequest rcr, HttpResponseBuilder results) {
     String contentType = results.getHeader("Content-Type");
     if (!StringUtils.isEmpty(rcr.getRewriteMimeType())) {
@@ -154,4 +159,19 @@ public class ProxyHandler {
       response.setHeader("Content-Type", "application/octet-stream");
     }
   }
+
+  /**
+   * Returns true in case the error encountered while rewriting the content
+   * is recoverable. The rationale behind it is that errors should be thrown
+   * only in case of serious grave errors (defined to be un recoverable).
+   * It should always be preferred to handle errors and return the original
+   * content at least.
+   *
+   * @param results The result of rewriting.
+   * @return True if the error is recoverable, false otherwise.
+   */
+  public boolean isRecoverable(HttpResponse results) {
+    return !(StringUtils.isEmpty(results.getResponseAsString()) &&
+             results.getHeaders() == null);
+  }
 }

Modified: shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/uri/ProxyUriManager.java
URL: http://svn.apache.org/viewvc/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/uri/ProxyUriManager.java?rev=986785&r1=986784&r2=986785&view=diff
==============================================================================
--- shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/uri/ProxyUriManager.java (original)
+++ shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/uri/ProxyUriManager.java Wed Aug 18 16:53:02 2010
@@ -18,6 +18,7 @@
  */
 package org.apache.shindig.gadgets.uri;
 
+import com.google.common.annotations.VisibleForTesting;
 import com.google.common.base.Objects;
 import com.google.common.collect.Lists;
 
@@ -48,6 +49,10 @@ public interface ProxyUriManager {
     private Integer resizeWidth;
     private Integer resizeQuality;
     private boolean resizeNoExpand;
+
+    // If "true" then the original content should be returned to the user
+    // instead of internal server errors.
+    private String returnOriginalContentOnError;
     
     public ProxyUri(Gadget gadget, Uri resource) {
       super(gadget);
@@ -64,6 +69,11 @@ public interface ProxyUriManager {
       super(status, base);
       this.resource = resource;
     }
+
+    @VisibleForTesting
+    public void setReturnOriginalContentOnError(boolean returnOriginalContentOnError) {
+      this.returnOriginalContentOnError = returnOriginalContentOnError ? "1" : null;
+    }
     
     @Override
     public boolean equals(Object obj) {
@@ -80,13 +90,14 @@ public interface ProxyUriManager {
           && Objects.equal(this.resizeHeight, objUri.resizeHeight)
           && Objects.equal(this.resizeWidth, objUri.resizeWidth)
           && Objects.equal(this.resizeQuality, objUri.resizeQuality)
-          && this.resizeNoExpand == objUri.resizeNoExpand);
+          && Objects.equal(this.resizeNoExpand, objUri.resizeNoExpand)
+          && Objects.equal(this.returnOriginalContentOnError, objUri.returnOriginalContentOnError));
     }
 
     @Override
     public int hashCode() {
       return Objects.hashCode(super.hashCode(), resource, fallbackUrl, resizeHeight,
-              resizeWidth, resizeQuality, resizeNoExpand);
+              resizeWidth, resizeQuality, resizeNoExpand, returnOriginalContentOnError);
     }
 
     /* (non-Javadoc)
@@ -101,6 +112,8 @@ public interface ProxyUriManager {
         resizeWidth = getIntegerValue(uri.getQueryParameter(Param.RESIZE_WIDTH.getKey()));
         resizeQuality = getIntegerValue(uri.getQueryParameter(Param.RESIZE_QUALITY.getKey()));
         resizeNoExpand = getBooleanValue(uri.getQueryParameter(Param.NO_EXPAND.getKey()));
+        returnOriginalContentOnError = uri.getQueryParameter(
+            Param.RETURN_ORIGINAL_CONTENT_ON_ERROR.getKey());
       }
     }
 
@@ -135,6 +148,11 @@ public interface ProxyUriManager {
       }
     }
 
+    public boolean shouldReturnOrigOnErr() {
+      return "1".equals(this.returnOriginalContentOnError) ||
+             "true".equalsIgnoreCase(this.returnOriginalContentOnError);
+    }
+
     @Override
     public UriBuilder makeQueryParams(Integer forcedRefresh, String version) {
       UriBuilder builder = super.makeQueryParams(forcedRefresh, version);
@@ -153,6 +171,10 @@ public interface ProxyUriManager {
       if (fallbackUrl != null) {
         builder.addQueryParameter(Param.FALLBACK_URL_PARAM.getKey(), fallbackUrl);
       }
+      if (returnOriginalContentOnError != null) {
+        builder.addQueryParameter(Param.RETURN_ORIGINAL_CONTENT_ON_ERROR.getKey(),
+                                  returnOriginalContentOnError);
+      }
       return builder;
     }
     

Modified: shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/uri/UriCommon.java
URL: http://svn.apache.org/viewvc/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/uri/UriCommon.java?rev=986785&r1=986784&r2=986785&view=diff
==============================================================================
--- shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/uri/UriCommon.java (original)
+++ shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/uri/UriCommon.java Wed Aug 18 16:53:02 2010
@@ -48,6 +48,7 @@ public interface UriCommon {
     RESIZE_QUALITY("resize_q"),
     NO_EXPAND("no_expand"),
     FALLBACK_URL_PARAM("fallback_url"),
+    RETURN_ORIGINAL_CONTENT_ON_ERROR("rooe"),
     
     // This is a legacy param, superseded by container.
     @Deprecated

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=986785&r1=986784&r2=986785&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 Wed Aug 18 16:53:02 2010
@@ -18,16 +18,13 @@
  */
 package org.apache.shindig.gadgets.servlet;
 
-import static org.easymock.EasyMock.capture;
-import static org.easymock.EasyMock.expect;
-import static org.easymock.EasyMock.isA;
-
 import com.google.common.base.Objects;
 import com.google.common.collect.Maps;
 
 import org.apache.shindig.common.EasyMockTestCase;
 import org.apache.shindig.common.uri.Uri;
 import org.apache.shindig.config.ContainerConfig;
+import org.apache.shindig.gadgets.Gadget;
 import org.apache.shindig.gadgets.GadgetException;
 import org.apache.shindig.gadgets.http.HttpRequest;
 import org.apache.shindig.gadgets.http.HttpResponse;
@@ -35,12 +32,18 @@ import org.apache.shindig.gadgets.http.H
 import org.apache.shindig.gadgets.http.RequestPipeline;
 import org.apache.shindig.gadgets.rewrite.CaptureRewriter;
 import org.apache.shindig.gadgets.rewrite.DefaultResponseRewriterRegistry;
+import org.apache.shindig.gadgets.rewrite.DomWalker;
+import org.apache.shindig.gadgets.rewrite.MutableContent;
 import org.apache.shindig.gadgets.rewrite.ResponseRewriter;
 import org.apache.shindig.gadgets.rewrite.ResponseRewriterRegistry;
+import org.apache.shindig.gadgets.rewrite.RewritingException;
 import org.apache.shindig.gadgets.uri.ProxyUriManager;
 import org.apache.shindig.gadgets.uri.UriCommon.Param;
 import org.easymock.Capture;
-
+import org.easymock.EasyMock;
+import static org.easymock.EasyMock.capture;
+import static org.easymock.EasyMock.expect;
+import static org.easymock.EasyMock.isA;
 import org.junit.Test;
 
 import java.util.Arrays;
@@ -87,6 +90,23 @@ public class ProxyHandlerTest extends Ea
         url != null ? Uri.parse(url) : null);
   }
 
+  private ResponseRewriter getResponseRewriterThatThrowsExceptions(
+      final StringBuilder stringBuilder) {
+    return new DomWalker.Rewriter() {
+      public void rewrite(Gadget gadget, MutableContent content)
+          throws RewritingException {
+        stringBuilder.append("exceptionThrown");
+        throw new RewritingException("sad", 404);
+      }
+
+      public void rewrite(HttpRequest request, HttpResponseBuilder builder)
+          throws RewritingException {
+        stringBuilder.append("exceptionThrown");
+        throw new RewritingException("sad", 404);
+      }
+    };
+  }
+
   @Test
   public void testLockedDomainEmbed() throws Exception {
     setupNoArgsProxyRequestMock("www.example.com", URL_ONE);
@@ -225,6 +245,80 @@ public class ProxyHandlerTest extends Ea
     verify();
   }
 
+  // ProxyHandler throws INTERNAL_SERVER_ERRORS without isRecoverable() check.
+  @Test
+  public void testRecoverableRewritingException() throws Exception {
+    String url = "http://example.org/mypage.html";
+    String domain = "example.org";
+
+    setupProxyRequestMock(domain, url, true, -1, null, null);
+
+    String contentType = "text/html; charset=UTF-8";
+    HttpResponse resp = new HttpResponseBuilder()
+        .setResponseString("Hello")
+        .addHeader("Content-Type", contentType)
+        .create();
+
+    expect(pipeline.execute((HttpRequest) EasyMock.anyObject())).andReturn(resp);
+
+    replay();
+
+    final StringBuilder stringBuilder = new StringBuilder("");
+    ResponseRewriter rewriter = getResponseRewriterThatThrowsExceptions(stringBuilder);
+
+    ResponseRewriterRegistry rewriterRegistry =
+        new DefaultResponseRewriterRegistry(
+            Arrays.<ResponseRewriter>asList(rewriter), null);
+    ProxyHandler proxyHandler = new ProxyHandler(pipeline, rewriterRegistry);
+
+    request.setReturnOriginalContentOnError(true);
+    HttpResponse recorder = proxyHandler.fetch(request);
+
+    verify();
+
+    // Ensure that original content is returned.
+    assertEquals(recorder.getHeader("Content-Type"), contentType);
+    assertEquals("Hello", recorder.getResponseAsString());
+    assertEquals("exceptionThrown", stringBuilder.toString());
+  }
+
+  @Test
+  public void testThrowExceptionIfReturnOriginalContentOnErrorNotSet()
+      throws Exception {
+    String url = "http://example.org/mypage.html";
+    String domain = "example.org";
+
+    setupProxyRequestMock(domain, url, true, -1, null, null);
+
+    String contentType = "text/html; charset=UTF-8";
+    HttpResponse resp = new HttpResponseBuilder()
+        .setResponseString("Hello")
+        .addHeader("Content-Type", contentType)
+        .create();
+
+    expect(pipeline.execute((HttpRequest) EasyMock.anyObject())).andReturn(resp);
+
+    replay();
+
+    final StringBuilder stringBuilder = new StringBuilder("");
+    ResponseRewriter rewriter = getResponseRewriterThatThrowsExceptions(stringBuilder);
+
+    ResponseRewriterRegistry rewriterRegistry =
+        new DefaultResponseRewriterRegistry(
+            Arrays.<ResponseRewriter>asList(rewriter), null);
+    ProxyHandler proxyHandler = new ProxyHandler(pipeline, rewriterRegistry);
+
+    boolean exceptionCaught = false;
+    try {
+      proxyHandler.fetch(request);
+    } catch (GadgetException e) {
+      exceptionCaught = true;
+      assertEquals(404, e.getHttpStatusCode());
+    }
+    assertTrue(exceptionCaught);
+    assertEquals("exceptionThrown", stringBuilder.toString());
+  }
+
   /**
    * Override HttpRequest equals to check for cache control fields
    */