Author: johnh
Date: Mon Jul 26 22:12:54 2010
New Revision: 979468

URL: http://svn.apache.org/viewvc?rev=979468&view=rev
Log:
For AccelHandler/HtmlAccelServlet, return the same HTTP status code as that of 
the fetched resource. Emit the same error text as well in this case.

This is done w/ one caveat, to convert HTTP 500 to HTTP 502, done on a 
configurable basis (default=true). This is done to prevent common monitoring 
software from seeing Accel itself as faulty (status 500), vs. the server it's 
proxying.

Patch provided by Gagan Singh.


Modified:
    shindig/trunk/java/common/conf/shindig.properties
    
shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/servlet/AccelHandler.java
    
shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/uri/UriUtils.java
    
shindig/trunk/java/gadgets/src/test/java/org/apache/shindig/gadgets/servlet/HtmlAccelServletTest.java
    
shindig/trunk/java/gadgets/src/test/java/org/apache/shindig/gadgets/uri/UriUtilsTest.java

Modified: shindig/trunk/java/common/conf/shindig.properties
URL: 
http://svn.apache.org/viewvc/shindig/trunk/java/common/conf/shindig.properties?rev=979468&r1=979467&r2=979468&view=diff
==============================================================================
--- shindig/trunk/java/common/conf/shindig.properties (original)
+++ shindig/trunk/java/common/conf/shindig.properties Mon Jul 26 22:12:54 2010
@@ -141,3 +141,8 @@ org.apache.shindig.serviceExpirationDura
 #  both    - return both fields for full compatibility
 #
 shindig.json-rpc.result-field=result
+
+# Remap "Internal server error"s received from the basicHttpFetcherProxy 
server to
+# "Bad Gateway error"s, so that it is clear to the user that the proxy server 
is
+# the one that threw the exception.
+shindig.accelerate.remapInternalServerError=true

Modified: 
shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/servlet/AccelHandler.java
URL: 
http://svn.apache.org/viewvc/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/servlet/AccelHandler.java?rev=979468&r1=979467&r2=979468&view=diff
==============================================================================
--- 
shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/servlet/AccelHandler.java
 (original)
+++ 
shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/servlet/AccelHandler.java
 Mon Jul 26 22:12:54 2010
@@ -28,6 +28,7 @@ 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;
+import org.apache.shindig.gadgets.http.HttpResponseBuilder;
 import org.apache.shindig.gadgets.http.RequestPipeline;
 import org.apache.shindig.gadgets.rewrite.DomWalker;
 import org.apache.shindig.gadgets.rewrite.ResponseRewriterRegistry;
@@ -59,15 +60,19 @@ public class AccelHandler extends ProxyB
   protected final RequestPipeline requestPipeline;
   protected final ResponseRewriterRegistry contentRewriterRegistry;
   protected final AccelUriManager uriManager;
+  protected final boolean remapInternalServerError;
 
   @Inject
   public AccelHandler(RequestPipeline requestPipeline,
                       @Named("shindig.accelerate.response.rewriter.registry")
                       ResponseRewriterRegistry contentRewriterRegistry,
-                      AccelUriManager accelUriManager) {
+                      AccelUriManager accelUriManager,
+                      @Named("shindig.accelerate.remapInternalServerError")
+                      Boolean remapInternalServerError) {
     this.requestPipeline = requestPipeline;
     this.contentRewriterRegistry = contentRewriterRegistry;
     this.uriManager = accelUriManager;
+    this.remapInternalServerError = remapInternalServerError;
   }
 
   @Override
@@ -83,26 +88,25 @@ public class AccelHandler extends ProxyB
     HttpRequest req = buildHttpRequest(request, proxyUri);
     HttpResponse results = requestPipeline.execute(req);
 
-    if (!handleErrors(results, response)) {
-      // In case of errors where we want to short circuit the rewriting and
-      // throw appropriate gadget exception.
-      return;
-    }
-
-    // Rewrite the content.
-    try {
-      results = contentRewriterRegistry.rewriteHttpResponse(req, results);
-    } catch (RewritingException e) {
-      if (!isRecoverable(req, results, e)) {
-        throw new GadgetException(GadgetException.Code.INTERNAL_SERVER_ERROR, 
e,
-                                  e.getHttpStatusCode());
+    HttpResponse errorResponse = handleErrors(results);
+    if (errorResponse == null) {
+      // No error. Lets rewrite the content.
+      try {
+        results = contentRewriterRegistry.rewriteHttpResponse(req, results);
+      } catch (RewritingException e) {
+        if (!isRecoverable(req, results, e)) {
+          throw new 
GadgetException(GadgetException.Code.INTERNAL_SERVER_ERROR, e,
+                                    e.getHttpStatusCode());
+        }
       }
+    } else {
+      results = errorResponse;
     }
 
     // Copy the response headers and status code to the final http servlet
     // response.
     UriUtils.copyResponseHeadersAndStatusCode(
-        results, response, true,
+        results, response, remapInternalServerError, true,
         UriUtils.DisallowedHeaders.OUTPUT_TRANSFER_DIRECTIVES,
         UriUtils.DisallowedHeaders.CLIENT_STATE_DIRECTIVES);
 
@@ -110,6 +114,7 @@ public class AccelHandler extends ProxyB
     // had the rewrite mime type header.
     rewriteContentType(req, response);
 
+    // Copy the content.
     IOUtils.copy(results.getResponse(), response.getOutputStream());
   }
 
@@ -210,27 +215,23 @@ public class AccelHandler extends ProxyB
   }
 
   /**
-   * Process errors when fetching uri using request pipeline by throwing
-   * GadgetException in error cases.
+   * Process errors when fetching uri using request pipeline and return the
+   * error response to be returned to the user if any.
    * @param results The http response returned by request pipeline.
-   * @param response The http servlet response to be returned to the user.
-   * @return True if there is no error, false otherwise.
-   * @throws IOException In case of errors.
+   * @return An HttpResponse instance encapsulating error message and status
+   *   code to be returned to the user in case of errors, null otherwise.
    */
-  protected boolean handleErrors(HttpResponse results, HttpServletResponse 
response)
-      throws IOException {
+  protected HttpResponse handleErrors(HttpResponse results) {
     if (results == null) {
-      response.sendError(HttpServletResponse.SC_BAD_REQUEST, 
ERROR_FETCHING_DATA);
-      return false;
+      return new HttpResponseBuilder()
+          .setHttpStatusCode(HttpServletResponse.SC_NOT_FOUND)
+          .setResponse(ERROR_FETCHING_DATA.getBytes())
+          .create();
     }
-    if (results.getHttpStatusCode() == HttpServletResponse.SC_NOT_FOUND) {
-      response.sendError(HttpServletResponse.SC_NOT_FOUND, 
ERROR_FETCHING_DATA);
-      return false;
-    } else if (results.isError()) {
-      response.sendError(HttpServletResponse.SC_BAD_GATEWAY, 
ERROR_FETCHING_DATA);
-      return false;
+    if (results.isError()) {
+      return results;
     }
 
-    return true;
+    return null;
   }
 }

Modified: 
shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/uri/UriUtils.java
URL: 
http://svn.apache.org/viewvc/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/uri/UriUtils.java?rev=979468&r1=979467&r2=979468&view=diff
==============================================================================
--- 
shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/uri/UriUtils.java
 (original)
+++ 
shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/uri/UriUtils.java
 Mon Jul 26 22:12:54 2010
@@ -108,6 +108,8 @@ public class UriUtils {
    *   response.
    * @param data The http response when fetching the requested accel uri.
    * @param resp The servlet response to return back to client.
+   * @param remapInternalServerError If true, then SC_INTERNAL_SERVER_ERROR is
+   *   remapped to SC_BAD_GATEWAY.
    * @param setHeaders If true, then setHeader method of HttpServletResponse is
    *   called, otherwise addHeader is called for every header.
    * @param disallowedResponseHeaders Disallowed response headers to omit from 
the response
@@ -116,6 +118,7 @@ public class UriUtils {
    */
   public static void copyResponseHeadersAndStatusCode(
       HttpResponse data, HttpServletResponse resp,
+      boolean remapInternalServerError,
       boolean setHeaders,
       DisallowedHeaders... disallowedResponseHeaders)
       throws IOException {
@@ -138,9 +141,11 @@ public class UriUtils {
       }
     }
 
-    // External "internal error" should be mapped to gateway error.
-    if (data.getHttpStatusCode() == HttpResponse.SC_INTERNAL_SERVER_ERROR) {
-      resp.sendError(HttpResponse.SC_BAD_GATEWAY);
+    if (remapInternalServerError) {
+      // External "internal error" should be mapped to gateway error.
+      if (data.getHttpStatusCode() == HttpResponse.SC_INTERNAL_SERVER_ERROR) {
+        resp.setStatus(HttpResponse.SC_BAD_GATEWAY);
+      }
     }
   }
 

Modified: 
shindig/trunk/java/gadgets/src/test/java/org/apache/shindig/gadgets/servlet/HtmlAccelServletTest.java
URL: 
http://svn.apache.org/viewvc/shindig/trunk/java/gadgets/src/test/java/org/apache/shindig/gadgets/servlet/HtmlAccelServletTest.java?rev=979468&r1=979467&r2=979468&view=diff
==============================================================================
--- 
shindig/trunk/java/gadgets/src/test/java/org/apache/shindig/gadgets/servlet/HtmlAccelServletTest.java
 (original)
+++ 
shindig/trunk/java/gadgets/src/test/java/org/apache/shindig/gadgets/servlet/HtmlAccelServletTest.java
 Mon Jul 26 22:12:54 2010
@@ -90,7 +90,7 @@ public class HtmlAccelServletTest extend
         Arrays.<ResponseRewriter>asList(rewriter), null);
     servlet = new HtmlAccelServlet();
     servlet.setHandler(new AccelHandler(pipeline, rewriterRegistry,
-                                             accelUriManager));
+                                        accelUriManager, true));
   }
 
   private void expectRequest(String extraPath, String url) {
@@ -149,19 +149,21 @@ public class HtmlAccelServletTest extend
 
     servlet.doGet(request, recorder);
     verify();
-    assertEquals("Error fetching data", recorder.getResponseAsString());
-    assertEquals(400, recorder.getHttpStatusCode());
+    assertEquals(AccelHandler.ERROR_FETCHING_DATA,
+                 recorder.getResponseAsString());
+    assertEquals(404, recorder.getHttpStatusCode());
   }
 
   @Test
-  public void testHtmlAccelNoHtml() throws Exception {
-    String url = "http://example.org/data.xml";;
+  public void testHtmlAccelRewriteSimple() throws Exception {
+    String url = "http://example.org/data.html";;
     String data = "<html><body>Hello World</body></html>";
 
+    ((FakeCaptureRewriter) rewriter).setContentToRewrite(REWRITE_CONTENT);
     HttpRequest req = new HttpRequest(Uri.parse(url));
     HttpResponse resp = new HttpResponseBuilder()
         .setResponse(data.getBytes())
-        .setHeader("Content-Type", "text/xml")
+        .setHeader("Content-Type", "text/html")
         .setHttpStatusCode(200)
         .create();
     expect(pipeline.execute(req)).andReturn(resp).once();
@@ -170,20 +172,24 @@ public class HtmlAccelServletTest extend
 
     servlet.doGet(request, recorder);
     verify();
-    assertEquals(data, recorder.getResponseAsString());
+    assertEquals(REWRITE_CONTENT, recorder.getResponseAsString());
+    assertEquals(200, recorder.getHttpStatusCode());
+    assertTrue(rewriter.responseWasRewritten());
   }
 
   @Test
-  public void testHtmlAccelRewriteSimple() throws Exception {
+  public void testHtmlAccelRewriteDoesNotFollowRedirects() throws Exception {
     String url = "http://example.org/data.html";;
-    String data = "<html><body>Hello World</body></html>";
+    String data = "<html><body>Moved to new page</body></html>";
+    String redirectLocation = "http://example-redirected.org/data.html";;
 
-    ((FakeCaptureRewriter) rewriter).setContentToRewrite(REWRITE_CONTENT);
+    ((FakeCaptureRewriter) rewriter).setContentToRewrite(data);
     HttpRequest req = new HttpRequest(Uri.parse(url));
     HttpResponse resp = new HttpResponseBuilder()
         .setResponse(data.getBytes())
         .setHeader("Content-Type", "text/html")
-        .setHttpStatusCode(200)
+        .setHeader("Location", redirectLocation)
+        .setHttpStatusCode(302)
         .create();
     expect(pipeline.execute(req)).andReturn(resp).once();
     expectRequest("", url);
@@ -191,13 +197,14 @@ public class HtmlAccelServletTest extend
 
     servlet.doGet(request, recorder);
     verify();
-    assertEquals(REWRITE_CONTENT, recorder.getResponseAsString());
-    assertEquals(200, recorder.getHttpStatusCode());
+    assertEquals(data, recorder.getResponseAsString());
+    assertEquals(redirectLocation, recorder.getHeader("Location"));
+    assertEquals(302, recorder.getHttpStatusCode());
     assertTrue(rewriter.responseWasRewritten());
   }
 
   @Test
-  public void testHtmlAccelRewriteErrorCode() throws Exception {
+  public void testHtmlAccelReturnsOriginal404MessageAndCode() throws Exception 
{
     String url = "http://example.org/data.html";;
     String data = "<html><body>This is error page</body></html>";
 
@@ -214,7 +221,7 @@ public class HtmlAccelServletTest extend
 
     servlet.doGet(request, recorder);
     verify();
-    assertEquals(AccelHandler.ERROR_FETCHING_DATA, 
recorder.getResponseAsString());
+    assertEquals(data, recorder.getResponseAsString());
     assertEquals(404, recorder.getHttpStatusCode());
     assertFalse(rewriter.responseWasRewritten());
   }
@@ -237,7 +244,7 @@ public class HtmlAccelServletTest extend
 
     servlet.doGet(request, recorder);
     verify();
-    assertEquals(AccelHandler.ERROR_FETCHING_DATA, 
recorder.getResponseAsString());
+    assertEquals(data, recorder.getResponseAsString());
     assertEquals(502, recorder.getHttpStatusCode());
     assertFalse(rewriter.responseWasRewritten());
   }
@@ -265,4 +272,27 @@ public class HtmlAccelServletTest extend
     assertEquals("POST", req.getValue().getMethod());
     assertEquals("hello=world", req.getValue().getPostBodyAsString());
   }
+
+  @Test
+  public void testHtmlAccelReturnsSameStatusCodeAndMessageWhenError() throws 
Exception {
+    String url = "http://example.org/data.html";;
+    String data = "<html><body>This is error page</body></html>";
+
+    ((FakeCaptureRewriter) rewriter).setContentToRewrite(data);
+    HttpRequest req = new HttpRequest(Uri.parse(url));
+    HttpResponse resp = new HttpResponseBuilder()
+        .setResponse(data.getBytes())
+        .setHeader("Content-Type", "text/html")
+        .setHttpStatusCode(5001)
+        .create();
+    expect(pipeline.execute(req)).andReturn(resp).once();
+    expectRequest("", url);
+    replay();
+
+    servlet.doGet(request, recorder);
+    verify();
+    assertEquals(data, recorder.getResponseAsString());
+    assertEquals(5001, recorder.getHttpStatusCode());
+    assertFalse(rewriter.responseWasRewritten());
+  }
 }

Modified: 
shindig/trunk/java/gadgets/src/test/java/org/apache/shindig/gadgets/uri/UriUtilsTest.java
URL: 
http://svn.apache.org/viewvc/shindig/trunk/java/gadgets/src/test/java/org/apache/shindig/gadgets/uri/UriUtilsTest.java?rev=979468&r1=979467&r2=979468&view=diff
==============================================================================
--- 
shindig/trunk/java/gadgets/src/test/java/org/apache/shindig/gadgets/uri/UriUtilsTest.java
 (original)
+++ 
shindig/trunk/java/gadgets/src/test/java/org/apache/shindig/gadgets/uri/UriUtilsTest.java
 Mon Jul 26 22:12:54 2010
@@ -75,7 +75,7 @@ public class UriUtilsTest extends EasyMo
     
 
     replay();
-    UriUtils.copyResponseHeadersAndStatusCode(resp, response, false,
+    UriUtils.copyResponseHeadersAndStatusCode(resp, response, false, false,
         UriUtils.DisallowedHeaders.OUTPUT_TRANSFER_DIRECTIVES,
         UriUtils.DisallowedHeaders.CACHING_DIRECTIVES);
     verify();
@@ -103,7 +103,63 @@ public class UriUtilsTest extends EasyMo
 
 
     replay();
-    UriUtils.copyResponseHeadersAndStatusCode(resp, response, true,
+    UriUtils.copyResponseHeadersAndStatusCode(resp, response, false, true,
+        UriUtils.DisallowedHeaders.OUTPUT_TRANSFER_DIRECTIVES,
+        UriUtils.DisallowedHeaders.CACHING_DIRECTIVES);
+    verify();
+  }
+
+  @Test
+  public void testCopyResponseHeadersAndStatusCode_RemapTrue() throws 
Exception {
+    HttpResponse resp = new HttpResponseBuilder()
+        .setHttpStatusCode(500)
+        .addHeader("hello", "world1")
+        .addHeader("hello", "world2")
+        .addHeader("hello\\u2297", "bad header")
+        .addHeader("Content-length", "10")
+        .addHeader("vary", "1")
+        .create();
+    HttpServletResponse response = mock(HttpServletResponse.class);
+
+    response.setStatus(502);
+    EasyMock.expectLastCall().once();
+
+    response.setHeader("hello", "world1");
+    EasyMock.expectLastCall().once();
+    response.setHeader("hello", "world2");
+    EasyMock.expectLastCall().once();
+
+
+    replay();
+    UriUtils.copyResponseHeadersAndStatusCode(resp, response, true, true,
+        UriUtils.DisallowedHeaders.OUTPUT_TRANSFER_DIRECTIVES,
+        UriUtils.DisallowedHeaders.CACHING_DIRECTIVES);
+    verify();
+  }
+
+  @Test
+  public void testCopyResponseHeadersAndStatusCode_RemapFalse() throws 
Exception {
+    HttpResponse resp = new HttpResponseBuilder()
+        .setHttpStatusCode(500)
+        .addHeader("hello", "world1")
+        .addHeader("hello", "world2")
+        .addHeader("hello\\u2297", "bad header")
+        .addHeader("Content-length", "10")
+        .addHeader("vary", "1")
+        .create();
+    HttpServletResponse response = mock(HttpServletResponse.class);
+
+    response.setStatus(500);
+    EasyMock.expectLastCall().once();
+
+    response.setHeader("hello", "world1");
+    EasyMock.expectLastCall().once();
+    response.setHeader("hello", "world2");
+    EasyMock.expectLastCall().once();
+
+
+    replay();
+    UriUtils.copyResponseHeadersAndStatusCode(resp, response, false, true,
         UriUtils.DisallowedHeaders.OUTPUT_TRANSFER_DIRECTIVES,
         UriUtils.DisallowedHeaders.CACHING_DIRECTIVES);
     verify();


Reply via email to