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
*/