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/07/19 19:57:38 UTC

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

Author: johnh
Date: Mon Jul 19 17:57:38 2010
New Revision: 965572

URL: http://svn.apache.org/viewvc?rev=965572&view=rev
Log:
Handle POST requests in Accel service.

Also includes several enhancements and tests for UriUtils.

Patch provided by Gagan Singh. Thanks!


Modified:
    shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/http/HttpRequest.java
    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/servlet/HtmlAccelServlet.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

Modified: shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/http/HttpRequest.java
URL: http://svn.apache.org/viewvc/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/http/HttpRequest.java?rev=965572&r1=965571&r2=965572&view=diff
==============================================================================
--- shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/http/HttpRequest.java (original)
+++ shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/http/HttpRequest.java Mon Jul 19 17:57:38 2010
@@ -51,6 +51,10 @@ public class HttpRequest {
   private String method = "GET";
   private Uri uri;
   private final Map<String, List<String>> headers = Maps.newTreeMap(String.CASE_INSENSITIVE_ORDER);
+
+  // Internal parameters which serve as extra information to pass along the
+  // chain of HttpRequest processing.
+  // NOTE: These are not get/post parameter equivalent of HttpServletRequest.
   private final Map<String, String> params = Maps.newHashMap();
 
   private byte[] postBody = ArrayUtils.EMPTY_BYTE_ARRAY;

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=965572&r1=965571&r2=965572&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 19 17:57:38 2010
@@ -102,9 +102,9 @@ public class AccelHandler extends ProxyB
     // Copy the response headers and status code to the final http servlet
     // response.
     UriUtils.copyResponseHeadersAndStatusCode(
-        results, response,
-        UriUtils.DisallowedResponseHeaders.OUTPUT_TRANSFER_DIRECTIVES,
-        UriUtils.DisallowedResponseHeaders.CLIENT_STATE_DIRECTIVES);
+        results, response, true,
+        UriUtils.DisallowedHeaders.OUTPUT_TRANSFER_DIRECTIVES,
+        UriUtils.DisallowedHeaders.CLIENT_STATE_DIRECTIVES);
 
     // Override the content type of the final http response if the input request
     // had the rewrite mime type header.
@@ -160,7 +160,8 @@ public class AccelHandler extends ProxyB
   }
 
   /**
-   * Generate a remote content request based on the parameters sent from the client.
+   * Build an HttpRequest object encapsulating the request details as requested
+   * by the user.
    * @param request The http request.
    * @param uriToProxyOrRewrite The parsed uri to proxy or rewrite through
    *   accel servlet.
@@ -173,12 +174,21 @@ public class AccelHandler extends ProxyB
     Uri tgt = uriToProxyOrRewrite.getResource();
     validateUrl(tgt);
     HttpRequest req = uriToProxyOrRewrite.makeHttpRequest(tgt);
-    this.setRequestHeaders(request, req);
-
     if (req == null) {
       throw new GadgetException(GadgetException.Code.INVALID_PARAMETER,
           "No url parameter in request", HttpResponse.SC_BAD_REQUEST);
     }
+
+    // Copy the post body if it exists.
+    UriUtils.copyRequestData(request, req);
+
+    // Set and copy headers.
+    this.setRequestHeaders(request, req);
+    UriUtils.copyRequestHeaders(
+        request, req,
+        UriUtils.DisallowedHeaders.POST_INCOMPATIBLE_DIRECTIVES);
+
+    req.setFollowRedirects(false);
     return req;
   }
 

Modified: shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/servlet/HtmlAccelServlet.java
URL: http://svn.apache.org/viewvc/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/servlet/HtmlAccelServlet.java?rev=965572&r1=965571&r2=965572&view=diff
==============================================================================
--- shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/servlet/HtmlAccelServlet.java (original)
+++ shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/servlet/HtmlAccelServlet.java Mon Jul 19 17:57:38 2010
@@ -62,4 +62,10 @@ public class HtmlAccelServlet extends In
     }
     accelHandler.fetch(request, response);
   }
+
+  @Override
+  protected void doPost(HttpServletRequest request, HttpServletResponse response)
+      throws IOException {
+    doGet(request, response);
+  }
 }

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=965572&r1=965571&r2=965572&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 19 17:57:38 2010
@@ -19,15 +19,17 @@
 package org.apache.shindig.gadgets.uri;
 
 import com.google.common.collect.ImmutableSet;
-
+import org.apache.shindig.gadgets.GadgetException;
+import org.apache.shindig.gadgets.http.HttpRequest;
 import org.apache.shindig.gadgets.http.HttpResponse;
 
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
 import java.io.IOException;
+import java.util.Enumeration;
+import java.util.HashSet;
 import java.util.Map;
 import java.util.Set;
-import java.util.HashSet;
-
-import javax.servlet.http.HttpServletResponse;
 
 /**
  * Utility functions related to URI and Http servlet response management.
@@ -38,7 +40,7 @@ public class UriUtils {
    * the user. The webserver serving out the response should be responsible
    * for filling these.
    */
-  public enum DisallowedResponseHeaders {
+  public enum DisallowedHeaders {
     // Directives controlled by the serving infrastructure.
     OUTPUT_TRANSFER_DIRECTIVES(ImmutableSet.of(
         "content-length", "transfer-encoding", "content-encoding", "server",
@@ -47,20 +49,25 @@ public class UriUtils {
     CACHING_DIRECTIVES(ImmutableSet.of("vary", "expires", "date", "pragma",
                                        "cache-control")),
 
-    CLIENT_STATE_DIRECTIVES(ImmutableSet.of("set-cookie", "www-authenticate"));
+    CLIENT_STATE_DIRECTIVES(ImmutableSet.of("set-cookie", "www-authenticate")),
+
+    // Headers that the fetcher itself would like to fill. For example,
+    // httpclient library crashes if Content-Length header is set in the
+    // request being fetched.
+    POST_INCOMPATIBLE_DIRECTIVES(ImmutableSet.of("content-length"));
 
     // Miscellaneous headers we should take care of, but are left for now.
     // "set-cookie", "content-length", "content-encoding", "etag",
     // "last-modified" ,"accept-ranges", "vary", "expires", "date",
     // "pragma", "cache-control", "transfer-encoding", "www-authenticate"
 
-    private Set<String> responseHeaders;
-    DisallowedResponseHeaders(Set<String> responseHeaders) {
-      this.responseHeaders = responseHeaders;
+    private Set<String> disallowedHeaders;
+    DisallowedHeaders(Set<String> disallowedHeaders) {
+      this.disallowedHeaders = disallowedHeaders;
     }
 
-    public Set<String> getResponseHeaders() {
-      return responseHeaders;
+    public Set<String> getDisallowedHeaders() {
+      return disallowedHeaders;
     }
   }
 
@@ -101,26 +108,33 @@ 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 disallowedResponseHeaderses Disallowed response headers to omit from the response
+   * @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
    *   returned to the user. 
    * @throws IOException In case the http response was not successful.
    */
   public static void copyResponseHeadersAndStatusCode(
       HttpResponse data, HttpServletResponse resp,
-      DisallowedResponseHeaders... disallowedResponseHeaderses)
+      boolean setHeaders,
+      DisallowedHeaders... disallowedResponseHeaders)
       throws IOException {
     // Pass original return code:
     resp.setStatus(data.getHttpStatusCode());
 
     Set<String> allDisallowedHeaders = new HashSet<String>();
-    for (DisallowedResponseHeaders h : disallowedResponseHeaderses) {
-      allDisallowedHeaders.addAll(h.getResponseHeaders());
+    for (DisallowedHeaders h : disallowedResponseHeaders) {
+      allDisallowedHeaders.addAll(h.getDisallowedHeaders());
     }
 
     for (Map.Entry<String, String> entry : data.getHeaders().entries()) {
       if (isValidHeaderName(entry.getKey()) && isValidHeaderValue(entry.getValue()) &&
           !allDisallowedHeaders.contains(entry.getKey().toLowerCase())) {
-        resp.setHeader(entry.getKey(), entry.getValue());
+        if (setHeaders) {
+          resp.setHeader(entry.getKey(), entry.getValue());
+        } else {
+          resp.addHeader(entry.getKey(), entry.getValue());
+        }
       }
     }
 
@@ -129,4 +143,66 @@ public class UriUtils {
       resp.sendError(HttpResponse.SC_BAD_GATEWAY);
     }
   }
+
+  /**
+   * Copies headers from HttpServletRequest object to HttpRequest object.
+   * @param data Servlet request to copy headers from.
+   * @param req The HttpRequest object to copy headers to.
+   * @param disallowedRequestHeaders Disallowed request headers to omit from
+   *   the servlet request
+   */
+  public static void copyRequestHeaders(HttpServletRequest data,
+                                        HttpRequest req,
+                                        DisallowedHeaders... disallowedRequestHeaders) {
+    Set<String> allDisallowedHeaders = new HashSet<String>();
+    for (DisallowedHeaders h : disallowedRequestHeaders) {
+      allDisallowedHeaders.addAll(h.getDisallowedHeaders());
+    }
+
+    Enumeration headerNames = data.getHeaderNames();
+    if (headerNames != null) {
+      while (headerNames.hasMoreElements()) {
+        Object headerObj = headerNames.nextElement();
+        if (!(headerObj instanceof String)) {
+          continue;
+        }
+
+        String header = (String) headerObj;
+        Enumeration headerValues = data.getHeaders(header);
+        if (headerValues != null && headerValues.hasMoreElements() &&
+            isValidHeaderName(header) &&
+            !allDisallowedHeaders.contains(header.toLowerCase())) {
+          // Remove existing values of this header.
+          req.removeHeader(header);
+
+          while (headerValues.hasMoreElements()) {
+            Object valueObj = headerValues.nextElement();
+            if (valueObj != null && valueObj instanceof String &&
+                isValidHeaderValue((String) valueObj)) {
+              // Add this header to data.
+              req.addHeader(header, (String) valueObj);
+            }
+          }
+        }
+      }
+    }
+  }
+
+  /**
+   * Copies the post data from HttpServletRequest object to HttpRequest object.
+   * @param request Servlet request to copy post data from.
+   * @param req The HttpRequest object to copy post data to.
+   * @throws GadgetException In case of errors.
+   */
+  public static void copyRequestData(HttpServletRequest request,
+                                     HttpRequest req) throws GadgetException {
+    req.setMethod(request.getMethod());
+    try {
+      if (request.getMethod().toLowerCase().equals("post")) {
+        req.setPostBody(request.getInputStream());
+      }
+    } catch (IOException e) {
+      throw new GadgetException(GadgetException.Code.INTERNAL_SERVER_ERROR, e);
+    }
+  }
 }

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=965572&r1=965571&r2=965572&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 19 17:57:38 2010
@@ -32,12 +32,18 @@ import org.apache.shindig.gadgets.rewrit
 import org.apache.shindig.gadgets.uri.AccelUriManager;
 import org.apache.shindig.gadgets.uri.DefaultAccelUriManager;
 import org.apache.shindig.gadgets.uri.DefaultProxyUriManager;
+import org.easymock.Capture;
+import org.easymock.EasyMock;
+import org.easymock.IAnswer;
 import org.junit.Before;
 import org.junit.Test;
 
+import javax.servlet.ServletInputStream;
+import java.io.IOException;
 import java.util.Arrays;
 import java.util.Map;
 
+import static org.easymock.EasyMock.capture;
 import static org.easymock.EasyMock.expect;
 
 public class HtmlAccelServletTest extends ServletTestFixture {
@@ -87,6 +93,51 @@ public class HtmlAccelServletTest extend
                                              accelUriManager));
   }
 
+  private void expectRequest(String extraPath, String url) {
+    expect(request.getMethod()).andReturn("GET").anyTimes();
+    expect(request.getServletPath()).andReturn(SERVLET).anyTimes();
+    expect(request.getScheme()).andReturn("http").anyTimes();
+    expect(request.getServerName()).andReturn("apache.org").anyTimes();
+    expect(request.getServerPort()).andReturn(-1).anyTimes();
+    expect(request.getRequestURI()).andReturn(SERVLET + extraPath).anyTimes();
+    expect(request.getRequestURL())
+        .andReturn(new StringBuffer("apache.org" + SERVLET + extraPath))
+        .anyTimes();
+    String queryParams = (url == null ? "" : "url=" + url + "&container=accel"
+                                             + "&gadget=test");
+    expect(request.getQueryString()).andReturn(queryParams).anyTimes();
+  }
+
+  private void expectPostRequest(String extraPath, String url,
+                                 final String data)
+      throws IOException {
+    expect(request.getMethod()).andReturn("POST").anyTimes();
+    expect(request.getServletPath()).andReturn(SERVLET).anyTimes();
+    expect(request.getScheme()).andReturn("http").anyTimes();
+    expect(request.getServerName()).andReturn("apache.org").anyTimes();
+    expect(request.getServerPort()).andReturn(-1).anyTimes();
+    expect(request.getRequestURI()).andReturn(SERVLET + extraPath).anyTimes();
+    expect(request.getRequestURL())
+        .andReturn(new StringBuffer("apache.org" + SERVLET + extraPath))
+        .anyTimes();
+    String queryParams = (url == null ? "" : "url=" + url + "&container=accel"
+                                             + "&gadget=test");
+    expect(request.getQueryString()).andReturn(queryParams).anyTimes();
+
+    ServletInputStream inputStream = mock(ServletInputStream.class);
+    expect(request.getInputStream()).andReturn(inputStream);
+    expect(inputStream.read((byte[]) EasyMock.anyObject()))
+        .andAnswer(new IAnswer<Integer>() {
+          public Integer answer() throws Throwable {
+            byte[] byteArray = (byte[]) EasyMock.getCurrentArguments()[0];
+            System.arraycopy(data.getBytes(), 0, byteArray, 0, data.length());
+            return data.length();
+          }
+        });
+    expect(inputStream.read((byte[]) EasyMock.anyObject()))
+        .andReturn(-1);
+  }
+
   @Test
   public void testHtmlAccelNoData() throws Exception {
     String url = "http://example.org/data.html";
@@ -191,17 +242,27 @@ public class HtmlAccelServletTest extend
     assertFalse(rewriter.responseWasRewritten());
   }
 
-  private void expectRequest(String extraPath, String url) {
-    expect(request.getServletPath()).andReturn(SERVLET).anyTimes();
-    expect(request.getScheme()).andReturn("http").anyTimes();
-    expect(request.getServerName()).andReturn("apache.org").anyTimes();
-    expect(request.getServerPort()).andReturn(-1).anyTimes();
-    expect(request.getRequestURI()).andReturn(SERVLET + extraPath).anyTimes();
-    expect(request.getRequestURL())
-        .andReturn(new StringBuffer("apache.org" + SERVLET + extraPath))
-        .anyTimes();
-    String queryParams = (url == null ? "" : "url=" + url + "&container=accel"
-                                             + "&gadget=test");
-    expect(request.getQueryString()).andReturn(queryParams).anyTimes();
+  @Test
+  public void testHtmlAccelHandlesPost() throws Exception {
+    String url = "http://example.org/data.html";
+    String data = "<html><body>This is error page</body></html>";
+
+    ((FakeCaptureRewriter) rewriter).setContentToRewrite(data);
+    Capture<HttpRequest> req = new Capture<HttpRequest>();
+    HttpResponse resp = new HttpResponseBuilder()
+        .setResponse(data.getBytes())
+        .setHeader("Content-Type", "text/html")
+        .create();
+    expect(pipeline.execute(capture(req))).andReturn(resp).once();
+    expectPostRequest("", url, "hello=world");
+    replay();
+
+    servlet.doPost(request, recorder);
+    verify();
+    assertEquals(data, recorder.getResponseAsString());
+    assertEquals(200, recorder.getHttpStatusCode());
+    assertTrue(rewriter.responseWasRewritten());
+    assertEquals("POST", req.getValue().getMethod());
+    assertEquals("hello=world", req.getValue().getPostBodyAsString());
   }
 }