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/04 02:09:37 UTC

svn commit: r982090 [1/2] - in /shindig/trunk/java: common/src/main/java/org/apache/shindig/common/servlet/ gadgets/src/main/java/org/apache/shindig/gadgets/servlet/ gadgets/src/main/java/org/apache/shindig/gadgets/uri/ gadgets/src/test/java/org/apache...

Author: johnh
Date: Wed Aug  4 00:09:36 2010
New Revision: 982090

URL: http://svn.apache.org/viewvc?rev=982090&view=rev
Log:
Refactor proxy services, part #1: remove ProxyBase in favor of util methods.


Added:
    shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/servlet/ServletUtil.java
    shindig/trunk/java/gadgets/src/test/java/org/apache/shindig/gadgets/servlet/ServletUtilTest.java
Removed:
    shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/servlet/ProxyBase.java
    shindig/trunk/java/gadgets/src/test/java/org/apache/shindig/gadgets/servlet/ProxyBaseTest.java
Modified:
    shindig/trunk/java/common/src/main/java/org/apache/shindig/common/servlet/HttpUtil.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/servlet/MakeRequestHandler.java
    shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/servlet/MakeRequestServlet.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/servlet/ProxyServlet.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/servlet/MakeRequestHandlerTest.java
    shindig/trunk/java/gadgets/src/test/java/org/apache/shindig/gadgets/servlet/MakeRequestServletTest.java
    shindig/trunk/java/gadgets/src/test/java/org/apache/shindig/gadgets/servlet/ProxyHandlerTest.java
    shindig/trunk/java/gadgets/src/test/java/org/apache/shindig/gadgets/servlet/ProxyServletTest.java
    shindig/trunk/java/gadgets/src/test/java/org/apache/shindig/gadgets/uri/UriUtilsTest.java

Modified: shindig/trunk/java/common/src/main/java/org/apache/shindig/common/servlet/HttpUtil.java
URL: http://svn.apache.org/viewvc/shindig/trunk/java/common/src/main/java/org/apache/shindig/common/servlet/HttpUtil.java?rev=982090&r1=982089&r2=982090&view=diff
==============================================================================
--- shindig/trunk/java/common/src/main/java/org/apache/shindig/common/servlet/HttpUtil.java (original)
+++ shindig/trunk/java/common/src/main/java/org/apache/shindig/common/servlet/HttpUtil.java Wed Aug  4 00:09:36 2010
@@ -18,11 +18,16 @@
  */
 package org.apache.shindig.common.servlet;
 
+import org.apache.shindig.common.Pair;
+import org.apache.shindig.common.util.DateUtil;
 import org.apache.shindig.common.util.TimeSource;
 
+import com.google.common.collect.Lists;
+
 import javax.servlet.http.HttpServletRequest;
 import javax.servlet.http.HttpServletResponse;
 import java.util.Collection;
+import java.util.List;
 import java.util.regex.Pattern;
 
 /**
@@ -91,18 +96,28 @@ public final class HttpUtil {
    * @param noProxy True if you don't want the response to be cacheable by proxies.
    */
   public static void setCachingHeaders(HttpServletResponse response, int ttl, boolean noProxy) {
-    response.setDateHeader("Expires", timeSource.currentTimeMillis() + (1000L * ttl));
-
-    if (ttl == 0) {
-      response.setHeader("Pragma", "no-cache");
-      response.setHeader("Cache-Control", "no-cache");
+    for (Pair<String, String> header : getCachingHeadersToSet(ttl, noProxy)) {
+      response.setHeader(header.one, header.two);
+    }
+  }
+  
+  public static List<Pair<String, String>> getCachingHeadersToSet(int ttl, boolean noProxy) {
+    List<Pair<String, String>> cachingHeaders = Lists.newArrayListWithExpectedSize(3);
+    cachingHeaders.add(Pair.of("Expires",
+        DateUtil.formatRfc1123Date(timeSource.currentTimeMillis() + (1000L * ttl))));
+
+    if (ttl <= 0) {
+      cachingHeaders.add(Pair.of("Pragma", "no-cache"));
+      cachingHeaders.add(Pair.of("Cache-Control", "no-cache"));
     } else {
       if (noProxy) {
-        response.setHeader("Cache-Control", "private,max-age=" + Integer.toString(ttl));
+        cachingHeaders.add(Pair.of("Cache-Control", "private,max-age=" + Integer.toString(ttl)));
       } else {
-        response.setHeader("Cache-Control", "public,max-age=" + Integer.toString(ttl));
+        cachingHeaders.add(Pair.of("Cache-Control", "public,max-age=" + Integer.toString(ttl)));
       }
     }
+    
+    return cachingHeaders;
   }
 
   public static int getDefaultTtl() {

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=982090&r1=982089&r2=982090&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 Wed Aug  4 00:09:36 2010
@@ -23,7 +23,6 @@ import com.google.inject.name.Named;
 import org.apache.commons.io.IOUtils;
 import org.apache.commons.lang.StringUtils;
 import org.apache.shindig.common.uri.Uri;
-import org.apache.shindig.common.uri.UriBuilder;
 import org.apache.shindig.gadgets.Gadget;
 import org.apache.shindig.gadgets.GadgetException;
 import org.apache.shindig.gadgets.http.HttpRequest;
@@ -38,8 +37,7 @@ import org.apache.shindig.gadgets.uri.Pr
 import org.apache.shindig.gadgets.uri.UriCommon;
 import org.apache.shindig.gadgets.uri.UriUtils;
 
-import javax.servlet.http.HttpServletRequest;
-import javax.servlet.http.HttpServletResponse;
+import java.io.ByteArrayOutputStream;
 import java.io.IOException;
 
 /**
@@ -47,7 +45,7 @@ import java.io.IOException;
  * The objective is to accelerate web pages.
  */
 @Singleton
-public class AccelHandler extends ProxyBase {
+public class AccelHandler {
   static final String ERROR_FETCHING_DATA = "Error fetching data";
 
   // TODO: parameterize these.
@@ -72,14 +70,11 @@ public class AccelHandler extends ProxyB
     this.remapInternalServerError = remapInternalServerError;
   }
 
-  @Override
-  protected void doFetch(HttpServletRequest request, HttpServletResponse response)
-      throws IOException, GadgetException {
+  protected HttpResponse fetch(HttpRequest request) throws IOException, GadgetException {
     // TODO: Handle if modified since headers.
 
     // Parse and normalize to get a proxied request uri.
-    Uri requestUri = new UriBuilder(request).toUri();
-    ProxyUriManager.ProxyUri proxyUri = getProxyUri(requestUri);
+    ProxyUriManager.ProxyUri proxyUri = getProxyUri(request.getUri());
 
     // Fetch the content of the requested uri.
     HttpRequest req = buildHttpRequest(request, proxyUri);
@@ -102,6 +97,7 @@ public class AccelHandler extends ProxyB
 
     // Copy the response headers and status code to the final http servlet
     // response.
+    HttpResponseBuilder response = new HttpResponseBuilder();
     UriUtils.copyResponseHeadersAndStatusCode(
         results, response, remapInternalServerError, true,
         UriUtils.DisallowedHeaders.OUTPUT_TRANSFER_DIRECTIVES,
@@ -112,7 +108,11 @@ public class AccelHandler extends ProxyB
     UriUtils.maybeRewriteContentType(req, response);
 
     // Copy the content.
-    IOUtils.copy(results.getResponse(), response.getOutputStream());
+    // TODO: replace this with streaming APIs when ready
+    ByteArrayOutputStream baos = new ByteArrayOutputStream();
+    IOUtils.copy(results.getResponse(), baos);
+    response.setResponseNoCopy(baos.toByteArray());
+    return response.create();
   }
 
   /**
@@ -132,7 +132,7 @@ public class AccelHandler extends ProxyB
     } catch (Uri.UriException e) {
       throw new GadgetException(GadgetException.Code.INTERNAL_SERVER_ERROR,
                                 "Failed to parse uri: " + uriString,
-                                HttpServletResponse.SC_BAD_GATEWAY);
+                                HttpResponse.SC_BAD_GATEWAY);
     }
 
     Gadget gadget = DomWalker.makeGadget(requestUri);
@@ -170,11 +170,10 @@ public class AccelHandler extends ProxyB
    * @return Remote content request based on the parameters sent from the client.
    * @throws GadgetException In case the data could not be fetched.
    */
-  protected HttpRequest buildHttpRequest(HttpServletRequest request,
+  protected HttpRequest buildHttpRequest(HttpRequest request,
                                          ProxyUriManager.ProxyUri uriToProxyOrRewrite)
       throws GadgetException {
     Uri tgt = uriToProxyOrRewrite.getResource();
-    validateUrl(tgt);
     HttpRequest req = uriToProxyOrRewrite.makeHttpRequest(tgt);
     if (req == null) {
       throw new GadgetException(GadgetException.Code.INVALID_PARAMETER,
@@ -185,7 +184,8 @@ public class AccelHandler extends ProxyB
     UriUtils.copyRequestData(request, req);
 
     // Set and copy headers.
-    this.setRequestHeaders(request, req);
+    ServletUtil.setXForwardedForHeader(request, req);
+    
     UriUtils.copyRequestHeaders(
         request, req,
         UriUtils.DisallowedHeaders.POST_INCOMPATIBLE_DIRECTIVES);
@@ -204,7 +204,7 @@ public class AccelHandler extends ProxyB
   protected HttpResponse handleErrors(HttpResponse results) {
     if (results == null) {
       return new HttpResponseBuilder()
-          .setHttpStatusCode(HttpServletResponse.SC_NOT_FOUND)
+          .setHttpStatusCode(HttpResponse.SC_NOT_FOUND)
           .setResponse(ERROR_FETCHING_DATA.getBytes())
           .create();
     }

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=982090&r1=982089&r2=982090&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 Wed Aug  4 00:09:36 2010
@@ -19,6 +19,9 @@ package org.apache.shindig.gadgets.servl
 
 import com.google.inject.Inject;
 import org.apache.shindig.common.servlet.InjectedServlet;
+import org.apache.shindig.gadgets.GadgetException;
+import org.apache.shindig.gadgets.http.HttpRequest;
+import org.apache.shindig.gadgets.http.HttpResponse;
 
 import javax.servlet.ServletConfig;
 import javax.servlet.ServletException;
@@ -55,12 +58,21 @@ public class HtmlAccelServlet extends In
   }
 
   @Override
-  protected void doGet(HttpServletRequest request, HttpServletResponse response)
+  protected void doGet(HttpServletRequest request, HttpServletResponse servletResponse)
       throws IOException {
     if (logger.isLoggable(Level.FINE)) {
       logger.fine("Accel request = " + request.toString());
     }
-    accelHandler.fetch(request, response);
+    
+    HttpRequest req = ServletUtil.fromHttpServletRequest(request);
+    HttpResponse response = null;
+    try {
+      response = accelHandler.fetch(req);
+    } catch (GadgetException e) {
+      response = ServletUtil.errorResponse(e);
+    }
+    
+    ServletUtil.copyResponseToServlet(response, servletResponse);
   }
 
   @Override

Modified: shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/servlet/MakeRequestHandler.java
URL: http://svn.apache.org/viewvc/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/servlet/MakeRequestHandler.java?rev=982090&r1=982089&r2=982090&view=diff
==============================================================================
--- shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/servlet/MakeRequestHandler.java (original)
+++ shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/servlet/MakeRequestHandler.java Wed Aug  4 00:09:36 2010
@@ -25,8 +25,10 @@ import org.apache.commons.lang.StringUti
 import org.apache.shindig.auth.AuthInfo;
 import org.apache.shindig.auth.SecurityToken;
 import org.apache.shindig.common.JsonSerializer;
+import org.apache.shindig.common.servlet.HttpUtil;
 import org.apache.shindig.common.uri.Uri;
 import org.apache.shindig.common.util.Utf8UrlCoder;
+import org.apache.shindig.config.ContainerConfig;
 import org.apache.shindig.gadgets.AuthType;
 import org.apache.shindig.gadgets.FeedProcessor;
 import org.apache.shindig.gadgets.FetchResponseUtils;
@@ -38,6 +40,8 @@ import org.apache.shindig.gadgets.http.R
 import org.apache.shindig.gadgets.oauth.OAuthArguments;
 import org.apache.shindig.gadgets.rewrite.ResponseRewriterRegistry;
 import org.apache.shindig.gadgets.rewrite.RewritingException;
+import org.apache.shindig.gadgets.uri.UriCommon;
+import org.apache.shindig.gadgets.uri.UriCommon.Param;
 
 import java.io.IOException;
 import java.io.UnsupportedEncodingException;
@@ -53,13 +57,12 @@ import javax.servlet.http.HttpServletRes
  * Unlike ProxyHandler, this may perform operations such as OAuth or signed fetch.
  */
 @Singleton
-public class MakeRequestHandler extends ProxyBase {
+public class MakeRequestHandler {
   // Relaxed visibility for ease of integration. Try to avoid relying on these.
   public static final String UNPARSEABLE_CRUFT = "throw 1; < don't be evil' >";
   public static final String POST_DATA_PARAM = "postData";
   public static final String METHOD_PARAM = "httpMethod";
   public static final String HEADERS_PARAM = "headers";
-  public static final String NOCACHE_PARAM = "nocache";
   public static final String CONTENT_TYPE_PARAM = "contentType";
   public static final String NUM_ENTRIES_PARAM = "numEntries";
   public static final String DEFAULT_NUM_ENTRIES = "3";
@@ -80,8 +83,7 @@ public class MakeRequestHandler extends 
   /**
    * Executes a request, returning the response as JSON to be handled by makeRequest.
    */
-  @Override
-  protected void doFetch(HttpServletRequest request, HttpServletResponse response)
+  public void fetch(HttpServletRequest request, HttpServletResponse response)
       throws GadgetException, IOException {
     HttpRequest rcr = buildHttpRequest(request);
 
@@ -116,18 +118,18 @@ public class MakeRequestHandler extends 
    * @throws GadgetException
    */
   protected HttpRequest buildHttpRequest(HttpServletRequest request) throws GadgetException {
-    String urlStr = request.getParameter(URL_PARAM);
+    String urlStr = request.getParameter(Param.URL.getKey());
     if (urlStr == null) {
       throw new GadgetException(GadgetException.Code.INVALID_PARAMETER,
-          URL_PARAM + " parameter is missing.", HttpResponse.SC_BAD_REQUEST);
+          Param.URL.getKey() + " parameter is missing.", HttpResponse.SC_BAD_REQUEST);
     }
     
     Uri url = null;
     try {
-      url = validateUrl(Uri.parse(urlStr));
+      url = ServletUtil.validateUrl(Uri.parse(urlStr));
     } catch (IllegalArgumentException e) {
       throw new GadgetException(GadgetException.Code.INVALID_PARAMETER,
-          "Invalid " + URL_PARAM + " parameter", HttpResponse.SC_BAD_REQUEST);
+          "Invalid " + Param.URL.getKey() + " parameter", HttpResponse.SC_BAD_REQUEST);
     }
 
     HttpRequest req = new HttpRequest(url)
@@ -157,18 +159,18 @@ public class MakeRequestHandler extends 
       req.addHeader("Content-Type", "application/x-www-form-urlencoded");
     }
 
-    req.setIgnoreCache("1".equals(request.getParameter(NOCACHE_PARAM)));
+    req.setIgnoreCache("1".equals(request.getParameter(Param.NO_CACHE.getKey())));
 
-    if (request.getParameter(GADGET_PARAM) != null) {
-      req.setGadget(Uri.parse(request.getParameter(GADGET_PARAM)));
+    if (request.getParameter(Param.GADGET.getKey()) != null) {
+      req.setGadget(Uri.parse(request.getParameter(Param.GADGET.getKey())));
     }
 
     // If the proxy request specifies a refresh param then we want to force the min TTL for
     // the retrieved entry in the cache regardless of the headers on the content when it
     // is fetched from the original source.
-    if (request.getParameter(REFRESH_PARAM) != null) {
+    if (request.getParameter(Param.REFRESH.getKey()) != null) {
       try {
-        req.setCacheTtl(Integer.parseInt(request.getParameter(REFRESH_PARAM)));
+        req.setCacheTtl(Integer.parseInt(request.getParameter(Param.REFRESH.getKey())));
       } catch (NumberFormatException nfe) {
         // Ignore
       }
@@ -176,7 +178,7 @@ public class MakeRequestHandler extends 
     // Allow the rewriter to use an externally forced mime type. This is needed
     // allows proper rewriting of <script src="x"/> where x is returned with
     // a content type like text/html which unfortunately happens all too often
-    req.setRewriteMimeType(request.getParameter(REWRITE_MIME_TYPE_PARAM));
+    req.setRewriteMimeType(request.getParameter(Param.REWRITE_MIME_TYPE.getKey()));
 
     // Figure out whether authentication is required
     AuthType auth = AuthType.parse(getParameter(request, AUTHZ_PARAM, null));
@@ -186,7 +188,7 @@ public class MakeRequestHandler extends 
       req.setOAuthArguments(new OAuthArguments(auth, request));
     }
 
-    this.setRequestHeaders(request, req);
+    ServletUtil.setXForwardedForHeader(request, req);
     return req;
   }
 
@@ -220,7 +222,7 @@ public class MakeRequestHandler extends 
       HttpResponse results) throws GadgetException {
     boolean getFullHeaders =
         Boolean.parseBoolean(getParameter(request, GET_FULL_HEADERS_PARAM, "false"));
-    String originalUrl = request.getParameter(ProxyBase.URL_PARAM);
+    String originalUrl = request.getParameter(Param.URL.getKey());
     String body = results.getResponseAsString();
     if (body.length() > 0) {
       if ("FEED".equals(request.getParameter(CONTENT_TYPE_PARAM))) {
@@ -267,4 +269,53 @@ public class MakeRequestHandler extends 
     int numEntries = Integer.parseInt(getParameter(req, NUM_ENTRIES_PARAM, DEFAULT_NUM_ENTRIES));
     return new FeedProcessor().process(url, xml, getSummaries, numEntries).toString();
   }
+  
+  /**
+   * Extracts the container name from the request.
+   */
+  @SuppressWarnings("deprecation")
+  static String getContainer(HttpServletRequest request) {
+    String container = request.getParameter(Param.CONTAINER.getKey());
+    if (container == null) {
+      container = request.getParameter(Param.SYND.getKey());
+    }
+    return container != null ? container : ContainerConfig.DEFAULT_CONTAINER;
+  }
+  
+  /**
+   * getParameter helper method, returning default value if param not present.
+   */
+  static String getParameter(HttpServletRequest request, String key, String defaultValue) {
+    String ret = request.getParameter(key);
+    return ret != null ? ret : defaultValue;
+  }
+  
+  /**
+   * Sets cache control headers for the response.
+   */
+  @SuppressWarnings("boxing")
+  static void setResponseHeaders(HttpServletRequest request,
+      HttpServletResponse response, HttpResponse results) throws GadgetException {
+    int refreshInterval = 0;
+    if (results.isStrictNoCache() || "1".equals(request.getParameter(UriCommon.Param.NO_CACHE.getKey()))) {
+      refreshInterval = 0;
+    } else if (request.getParameter(UriCommon.Param.REFRESH.getKey()) != null) {
+      try {
+        refreshInterval =  Integer.valueOf(request.getParameter(UriCommon.Param.REFRESH.getKey()));
+      } catch (NumberFormatException nfe) {
+        throw new GadgetException(GadgetException.Code.INVALID_PARAMETER,
+            "refresh parameter is not a number");
+      }
+    } else {
+      refreshInterval = Math.max(60 * 60, (int)(results.getCacheTtl() / 1000L));
+    }
+    HttpUtil.setCachingHeaders(response, refreshInterval, false);
+    
+    // Always set Content-Disposition header as XSS prevention mechanism.
+    response.setHeader("Content-Disposition", "attachment;filename=p.txt");
+    
+    if (response.getContentType() == null) {
+      response.setContentType("application/octet-stream");
+    }
+  }
 }

Modified: shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/servlet/MakeRequestServlet.java
URL: http://svn.apache.org/viewvc/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/servlet/MakeRequestServlet.java?rev=982090&r1=982089&r2=982090&view=diff
==============================================================================
--- shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/servlet/MakeRequestServlet.java (original)
+++ shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/servlet/MakeRequestServlet.java Wed Aug  4 00:09:36 2010
@@ -19,6 +19,7 @@
 package org.apache.shindig.gadgets.servlet;
 
 import org.apache.shindig.common.servlet.InjectedServlet;
+import org.apache.shindig.gadgets.GadgetException;
 
 import java.io.IOException;
 
@@ -61,7 +62,15 @@ public class MakeRequestServlet extends 
   @Override
   protected void doGet(HttpServletRequest request, HttpServletResponse response)
       throws IOException {
-    makeRequestHandler.fetch(request, response);
+    try {
+      makeRequestHandler.fetch(request, response);
+    } catch (GadgetException e) {
+      int responseCode = HttpServletResponse.SC_INTERNAL_SERVER_ERROR;
+      if (e.getCode() != GadgetException.Code.INTERNAL_SERVER_ERROR) {
+        responseCode = HttpServletResponse.SC_BAD_REQUEST;
+      }
+      response.sendError(responseCode, e.getMessage() != null ? e.getMessage() : "");
+    }
   }
 
   @Override

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=982090&r1=982089&r2=982090&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  4 00:09:36 2010
@@ -20,111 +20,90 @@ package org.apache.shindig.gadgets.servl
 
 import com.google.inject.Inject;
 import com.google.inject.Singleton;
-import com.google.inject.name.Named;
 
 import org.apache.commons.io.IOUtils;
 import org.apache.commons.lang.StringUtils;
-import org.apache.shindig.common.servlet.HttpUtil;
 import org.apache.shindig.common.uri.Uri;
-import org.apache.shindig.common.uri.UriBuilder;
 import org.apache.shindig.gadgets.GadgetException;
 import org.apache.shindig.gadgets.LockedDomainService;
 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.ResponseRewriterRegistry;
 import org.apache.shindig.gadgets.rewrite.RewritingException;
 import org.apache.shindig.gadgets.uri.ProxyUriManager;
 import org.apache.shindig.gadgets.uri.UriUtils;
+import org.apache.shindig.gadgets.uri.UriUtils.DisallowedHeaders;
 
+import java.io.ByteArrayOutputStream;
 import java.io.IOException;
-import java.util.Map;
 import java.util.logging.Logger;
 
-import javax.servlet.http.HttpServletRequest;
-import javax.servlet.http.HttpServletResponse;
-
 /**
  * Handles open proxy requests.
  */
 @Singleton
-public class ProxyHandler extends ProxyBase {
+public class ProxyHandler {
   private static final Logger LOG = Logger.getLogger(ProxyHandler.class.getName());
 
   // 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 LockedDomainService lockedDomainService;
   private final ResponseRewriterRegistry contentRewriterRegistry;
   private final ProxyUriManager proxyUriManager;
-  protected final boolean remapInternalServerError;
 
   @Inject
   public ProxyHandler(RequestPipeline requestPipeline,
                       LockedDomainService lockedDomainService,
                       ResponseRewriterRegistry contentRewriterRegistry,
-                      ProxyUriManager proxyUriManager,
-                      @Named("shindig.proxy.remapInternalServerError")
-                      Boolean remapInternalServerError) {
+                      ProxyUriManager proxyUriManager) {
     this.requestPipeline = requestPipeline;
     this.lockedDomainService = lockedDomainService;
     this.contentRewriterRegistry = contentRewriterRegistry;
     this.proxyUriManager = proxyUriManager;
-    this.remapInternalServerError = remapInternalServerError;
   }
 
   /**
    * Generate a remote content request based on the parameters sent from the client.
    */
-  private HttpRequest buildHttpRequest(HttpServletRequest request,
+  private HttpRequest buildHttpRequest(HttpRequest request,
       ProxyUriManager.ProxyUri uriCtx, Uri tgt) throws GadgetException {
-    validateUrl(tgt);
+    ServletUtil.validateUrl(tgt);
     HttpRequest req = uriCtx.makeHttpRequest(tgt);
-    this.setRequestHeaders(request, req);
+    ServletUtil.setXForwardedForHeader(request, req);
     return req;
   }
 
-  @Override
-  protected void doFetch(HttpServletRequest request, HttpServletResponse response)
+  public HttpResponse fetch(HttpRequest request)
       throws IOException, GadgetException {
-    if (request.getHeader("If-Modified-Since") != null) {
-      response.setStatus(HttpServletResponse.SC_NOT_MODIFIED);
-      return;
-    }
+    // Parse request uri:
+    ProxyUriManager.ProxyUri proxyUri = proxyUriManager.process(request.getUri());
 
     // TODO: Consider removing due to redundant logic.
     String host = request.getHeader("Host");
     if (!lockedDomainService.isSafeForOpenProxy(host)) {
       // Force embedded images and the like to their own domain to avoid XSS
       // in gadget domains.
-      String msg = "Embed request for url " + getParameter(request, URL_PARAM, "") +
-          " made to wrong domain " + host;
+      Uri resourceUri = proxyUri.getResource();
+      String msg = "Embed request for url " +
+          (resourceUri != null ? resourceUri.toString() : "n/a") + " made to wrong domain " + host;
       LOG.info(msg);
       throw new GadgetException(GadgetException.Code.INVALID_PARAMETER, msg,
           HttpResponse.SC_BAD_REQUEST);
     }
 
-    // Parse request uri:
-    ProxyUriManager.ProxyUri proxyUri = proxyUriManager.process(
-        new UriBuilder(request).toUri());
-
-    try {
-      HttpUtil.setCachingHeaders(response,
-          proxyUri.translateStatusRefresh(LONG_LIVED_REFRESH, DEFAULT_REFRESH), false);
-    } catch (GadgetException gex) {
-      response.sendError(HttpServletResponse.SC_BAD_REQUEST, gex.getMessage());
-      return;
-    }
-
     HttpRequest rcr = buildHttpRequest(request, proxyUri, proxyUri.getResource());
     if (rcr == null) {
       throw new GadgetException(GadgetException.Code.INVALID_PARAMETER,
-          "No url paramater 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();
@@ -133,7 +112,7 @@ public class ProxyHandler extends ProxyB
         results = requestPipeline.execute(fallbackRcr);
       }
     }
-
+    
     if (contentRewriterRegistry != null) {
       try {
         results = contentRewriterRegistry.rewriteHttpResponse(rcr, results);
@@ -142,21 +121,63 @@ public class ProxyHandler extends ProxyB
             e.getHttpStatusCode());
       }
     }
-
-    // Copy the response headers and status code to the final http servlet
-    // response.
-    UriUtils.copyResponseHeadersAndStatusCode(
-        results, response, remapInternalServerError, false,
-        UriUtils.DisallowedHeaders.OUTPUT_TRANSFER_DIRECTIVES,
-        UriUtils.DisallowedHeaders.CACHING_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.
-    UriUtils.maybeRewriteContentType(rcr, response);
-
+    
+    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())) {
+      String requiredType = rcr.getRewriteMimeType();
+      // Use a 'Vary' style check on the response
+      if (requiredType.endsWith("/*") &&
+          !StringUtils.isEmpty(contentType)) {
+        requiredType = requiredType.substring(0, requiredType.length() - 2);
+        if (!contentType.toLowerCase().startsWith(requiredType.toLowerCase())) {
+          contentType = requiredType;
+        }
+      } else {
+        contentType = requiredType;
+      }
+    }
+    return contentType;
+  }
 
-    IOUtils.copy(results.getResponse(), response.getOutputStream());
+  private void setResponseContentHeaders(HttpResponseBuilder response, HttpResponse results) {
+    // We're skipping the content disposition header for flash due to an issue with Flash player 10
+    // This does make some sites a higher value phishing target, but this can be mitigated by
+    // additional referer checks.
+    if (!"application/x-shockwave-flash".equalsIgnoreCase(results.getHeader("Content-Type")) &&
+        !"application/x-shockwave-flash".equalsIgnoreCase(response.getHeader("Content-Type"))) {
+      response.setHeader("Content-Disposition", "attachment;filename=p.txt");
+    }
+    if (results.getHeader("Content-Type") == null) {
+      response.setHeader("Content-Type", "application/octet-stream");
+    }
   }
 }

Modified: shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/servlet/ProxyServlet.java
URL: http://svn.apache.org/viewvc/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/servlet/ProxyServlet.java?rev=982090&r1=982089&r2=982090&view=diff
==============================================================================
--- shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/servlet/ProxyServlet.java (original)
+++ shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/servlet/ProxyServlet.java Wed Aug  4 00:09:36 2010
@@ -19,6 +19,9 @@
 package org.apache.shindig.gadgets.servlet;
 
 import org.apache.shindig.common.servlet.InjectedServlet;
+import org.apache.shindig.gadgets.GadgetException;
+import org.apache.shindig.gadgets.http.HttpRequest;
+import org.apache.shindig.gadgets.http.HttpResponse;
 
 import java.io.IOException;
 
@@ -34,7 +37,6 @@ import com.google.inject.Inject;
  * gadgets.io.getProxyUrl).
  */
 public class ProxyServlet extends InjectedServlet {
-
   private static final long serialVersionUID = 9085050443492307723L;
   
   private transient ProxyHandler proxyHandler;
@@ -55,8 +57,22 @@ public class ProxyServlet extends Inject
   }
 
   @Override
-  protected void doGet(HttpServletRequest request, HttpServletResponse response)
+  protected void doGet(HttpServletRequest request, HttpServletResponse servletResponse)
       throws IOException {
-    proxyHandler.fetch(request, response);
+    if (request.getHeader("If-Modified-Since") != null) {
+      servletResponse.setStatus(HttpServletResponse.SC_NOT_MODIFIED);
+      return;
+    }
+    
+    HttpRequest req = ServletUtil.fromHttpServletRequest(request);
+    HttpResponse response = null;
+    try {
+      response = proxyHandler.fetch(req);
+    } catch (GadgetException e) {
+      response = ServletUtil.errorResponse(new GadgetException(e.getCode(), e.getMessage(),
+          HttpServletResponse.SC_BAD_REQUEST));
+    }
+    
+    ServletUtil.copyResponseToServlet(response, servletResponse);
   }
 }

Added: shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/servlet/ServletUtil.java
URL: http://svn.apache.org/viewvc/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/servlet/ServletUtil.java?rev=982090&view=auto
==============================================================================
--- shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/servlet/ServletUtil.java (added)
+++ shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/servlet/ServletUtil.java Wed Aug  4 00:09:36 2010
@@ -0,0 +1,209 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.shindig.gadgets.servlet;
+
+import org.apache.commons.codec.binary.Base64InputStream;
+import org.apache.commons.io.IOUtils;
+import org.apache.commons.lang.StringEscapeUtils;
+import org.apache.commons.lang.StringUtils;
+import org.apache.shindig.common.Pair;
+import org.apache.shindig.common.servlet.HttpUtil;
+import org.apache.shindig.common.uri.Uri;
+import org.apache.shindig.common.uri.UriBuilder;
+import org.apache.shindig.common.util.Utf8UrlCoder;
+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 java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.PrintWriter;
+import java.util.Enumeration;
+import java.util.Map;
+
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+public class ServletUtil {
+  public static final String REMOTE_ADDR_KEY = "RemoteAddress";
+  public static final String DATA_URI_KEY = "dataUri";
+  
+  public static HttpRequest fromHttpServletRequest(HttpServletRequest servletReq) throws IOException {
+    HttpRequest req = new HttpRequest(new UriBuilder(servletReq).toUri());
+    Enumeration<?> headerNames = servletReq.getHeaderNames();
+    while (headerNames.hasMoreElements()) {
+      String headerName = (String)headerNames.nextElement();
+      req.addHeader(headerName, servletReq.getHeader(headerName));
+    }
+    req.setMethod(servletReq.getMethod());
+    if ("POST".equalsIgnoreCase(req.getMethod())) {
+      req.setPostBody(servletReq.getInputStream());
+    }
+    req.setParam(REMOTE_ADDR_KEY, servletReq.getRemoteAddr());
+    return req;
+  }
+  
+  public static void setCachingHeaders(HttpResponseBuilder response, int ttl, boolean noProxy) {
+    for (Pair<String, String> header : HttpUtil.getCachingHeadersToSet(ttl, noProxy)) {
+      response.setHeader(header.one, header.two);
+    }
+  }
+  
+  public static void copyResponseToServlet(HttpResponse response, HttpServletResponse servletResponse)
+      throws IOException {
+    servletResponse.setStatus(response.getHttpStatusCode());
+    servletResponse.setContentLength(response.getContentLength());
+    for (Map.Entry<String, String> header : response.getHeaders().entries()) {
+      servletResponse.addHeader(header.getKey(), header.getValue());
+    }
+    HttpUtil.setCachingHeaders(servletResponse, (int)response.getCacheTtl());
+    IOUtils.copy(response.getResponse(), servletResponse.getOutputStream());
+  }
+  
+  /**
+   * Validates and normalizes the given url, ensuring that it is non-null, has
+   * scheme http or https, and has a path value of some kind.
+   *
+   * @return A URI representing a validated form of the url.
+   * @throws GadgetException If the url is not valid.
+   */
+  public static Uri validateUrl(Uri urlToValidate) throws GadgetException {
+    if (urlToValidate == null) {
+      throw new GadgetException(GadgetException.Code.MISSING_PARAMETER, "Missing url param",
+          HttpResponse.SC_BAD_REQUEST);
+    }
+    UriBuilder url = new UriBuilder(urlToValidate);
+    if (!"http".equals(url.getScheme()) && !"https".equals(url.getScheme())) {
+      throw new GadgetException(GadgetException.Code.INVALID_PARAMETER,
+          "Invalid request url scheme in url: " + Utf8UrlCoder.encode(urlToValidate.toString()) +
+          "; only \"http\" and \"https\" supported.", HttpResponse.SC_BAD_REQUEST);
+    }
+    if (url.getPath() == null || url.getPath().length() == 0) {
+      url.setPath("/");
+    }
+    return url.toUri();
+  }
+  
+  /**
+   * Sets standard forwarding headers on the proxied request.
+   * @param inboundRequest
+   * @param req
+   * @throws GadgetException
+   */
+  public static void setXForwardedForHeader(HttpRequest inboundRequest, HttpRequest req)
+      throws GadgetException {
+    String forwardedFor = getXForwardedForHeader(inboundRequest.getHeader("X-Forwarded-For"),
+        inboundRequest.getParam(ServletUtil.REMOTE_ADDR_KEY));
+    if (forwardedFor != null) {
+      req.setHeader("X-Forwarded-For", forwardedFor);
+    }
+  }
+
+  public static void setXForwardedForHeader(HttpServletRequest inboundRequest, HttpRequest req)
+      throws GadgetException {
+    String forwardedFor = getXForwardedForHeader(inboundRequest.getHeader("X-Forwarded-For"),
+        inboundRequest.getRemoteAddr());
+    if (forwardedFor != null) {
+      req.setHeader("X-Forwarded-For", forwardedFor);
+    }
+  }
+  
+  private static String getXForwardedForHeader(String origValue, String remoteAddr) {
+    if (!StringUtils.isEmpty(remoteAddr)) {
+      if (StringUtils.isEmpty(origValue)) {
+        origValue = remoteAddr;
+      } else {
+        origValue = remoteAddr + ", " + origValue;
+      }
+    }
+    return origValue;
+  }
+  
+  /**
+   * @return An HttpResponse object wrapping the given GadgetException.
+   */
+  public static HttpResponse errorResponse(GadgetException e) {
+    return new HttpResponseBuilder().setHttpStatusCode(e.getHttpStatusCode())
+        .setResponseString(e.getMessage() != null ? e.getMessage() : "").create();
+  }
+  
+  /**
+   * Converts the given {@code HttpResponse} into JSON form, with at least
+   * one field, dataUri, containing a Data URI that can be inlined into an HTML page.
+   * Any metadata on the given {@code HttpResponse} is also added as fields.
+   * 
+   * @param response Input HttpResponse to convert to JSON.
+   * @return JSON-containing HttpResponse.
+   * @throws IOException If there are problems reading from {@code response}.
+   */
+  public static HttpResponse convertToJsonResponse(HttpResponse response) throws IOException {
+    // Pull out charset, if present. If not, this operation simply returns contentType.
+    String contentType = response.getHeader("Content-Type");
+    if (contentType == null) {
+      contentType = "";
+    }
+    contentType = contentType.split(";")[0].trim();
+ 
+    // First and most importantly, emit dataUri.
+    // Do so in streaming fashion, to avoid needless buffering.
+    ByteArrayOutputStream os = new ByteArrayOutputStream();
+    PrintWriter pw = new PrintWriter(os);
+    pw.write("{\n  ");
+    pw.write(DATA_URI_KEY);
+    pw.write(":'data:");
+    pw.write(contentType);
+    pw.write(";base64;charset=");
+    pw.write(response.getEncoding());
+    pw.write(",");
+    pw.flush();
+    
+    // Stream out the base64-encoded data.
+    // Ctor args indicate to encode w/o line breaks.
+    Base64InputStream b64input = new Base64InputStream(response.getResponse(), true, 0, null);
+    byte[] buf = new byte[1024];
+    int read = -1;
+    while ((read = b64input.read(buf, 0, 1024)) > 0) {
+      os.write(buf, 0, read);
+    }
+    
+    // Complete the JSON object.
+    pw.write("',\n  ");
+    boolean first = true;
+    for (Map.Entry<String, String> metaEntry : response.getMetadata().entrySet()) {
+      if (DATA_URI_KEY.equals(metaEntry.getKey())) continue;
+      if (!first) {
+        pw.write(",\n  ");
+      }
+      first = false;
+      pw.write("'");
+      pw.write(StringEscapeUtils.escapeJavaScript(metaEntry.getKey()).replaceAll("'", "\\'"));
+      pw.write("':'");
+      pw.write(StringEscapeUtils.escapeJavaScript(metaEntry.getValue()).replaceAll("'", "\\'"));
+      pw.write("'");
+    }
+    pw.write("\n}");
+    pw.flush();
+    
+    return new HttpResponseBuilder()
+        .setHeader("Content-Type", "application/json")
+        .setResponseNoCopy(os.toByteArray())
+        .create();
+  }
+}

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=982090&r1=982089&r2=982090&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 Wed Aug  4 00:09:36 2010
@@ -19,17 +19,16 @@
 package org.apache.shindig.gadgets.uri;
 
 import com.google.common.collect.ImmutableSet;
+import org.apache.commons.lang.StringUtils;
 import org.apache.shindig.gadgets.GadgetException;
 import org.apache.shindig.gadgets.http.HttpRequest;
 import org.apache.shindig.gadgets.http.HttpResponse;
-import org.apache.commons.lang.StringUtils;
+import org.apache.shindig.gadgets.http.HttpResponseBuilder;
 
-import javax.servlet.http.HttpServletRequest;
-import javax.servlet.http.HttpServletResponse;
 import java.io.IOException;
 import java.util.logging.Logger;
-import java.util.Enumeration;
 import java.util.HashSet;
+import java.util.List;
 import java.util.Map;
 import java.util.Set;
 
@@ -135,13 +134,13 @@ public class UriUtils {
    * @throws IOException In case the http response was not successful.
    */
   public static void copyResponseHeadersAndStatusCode(
-      HttpResponse data, HttpServletResponse resp,
+      HttpResponse data, HttpResponseBuilder resp,
       boolean remapInternalServerError,
       boolean setHeaders,
       DisallowedHeaders... disallowedResponseHeaders)
       throws IOException {
     // Pass original return code:
-    resp.setStatus(data.getHttpStatusCode());
+    resp.setHttpStatusCode(data.getHttpStatusCode());
 
     Set<String> allDisallowedHeaders = new HashSet<String>();
     for (DisallowedHeaders h : disallowedResponseHeaders) {
@@ -167,19 +166,19 @@ public class UriUtils {
     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);
+        resp.setHttpStatusCode(HttpResponse.SC_BAD_GATEWAY);
       }
     }
   }
 
   /**
    * Copies headers from HttpServletRequest object to HttpRequest object.
-   * @param data Servlet request to copy headers from.
+   * @param origRequest 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,
+  public static void copyRequestHeaders(HttpRequest origRequest,
                                         HttpRequest req,
                                         DisallowedHeaders... disallowedRequestHeaders) {
     Set<String> allDisallowedHeaders = new HashSet<String>();
@@ -187,29 +186,18 @@ public class UriUtils {
       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);
-            }
+    for (Map.Entry<String, List<String>> inHeader : origRequest.getHeaders().entrySet()) {
+      String header = inHeader.getKey();
+      List<String> headerValues = inHeader.getValue();
+      
+      if (headerValues != null && headerValues.size() > 0 &&
+          isValidHeaderName(header) &&
+          !allDisallowedHeaders.contains(header.toLowerCase())) {
+        // Remove existing values of this header.
+        req.removeHeader(header);
+        for (String headerVal : headerValues) {
+          if (isValidHeaderValue(headerVal)) {
+            req.addHeader(header, headerVal);
           }
         }
       }
@@ -218,16 +206,16 @@ public class UriUtils {
 
   /**
    * Copies the post data from HttpServletRequest object to HttpRequest object.
-   * @param request Servlet request to copy post data from.
+   * @param origRequest 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,
+  public static void copyRequestData(HttpRequest origRequest,
                                      HttpRequest req) throws GadgetException {
-    req.setMethod(request.getMethod());
+    req.setMethod(origRequest.getMethod());
     try {
-      if (request.getMethod().toLowerCase().equals("post")) {
-        req.setPostBody(request.getInputStream());
+      if (origRequest.getMethod().toLowerCase().equals("post")) {
+        req.setPostBody(origRequest.getPostBody());
       }
     } catch (IOException e) {
       throw new GadgetException(GadgetException.Code.INTERNAL_SERVER_ERROR, e);
@@ -240,8 +228,8 @@ public class UriUtils {
    * @param req The http request.
    * @param response The final http response to be returned to user.
    */
-  public static void maybeRewriteContentType(HttpRequest req, HttpServletResponse response) {
-    String responseType = response.getContentType();
+  public static void maybeRewriteContentType(HttpRequest req, HttpResponseBuilder response) {
+    String responseType = response.getHeader("Content-Type");
     String requiredType = req.getRewriteMimeType();
     if (!StringUtils.isEmpty(requiredType)) {
       // Use a 'Vary' style check on the response
@@ -250,10 +238,10 @@ public class UriUtils {
         if (!responseType.toLowerCase().startsWith(requiredTypePrefix.toLowerCase())) {
           // TODO: We are currently setting the content type to something like x/* (e.g. text/*)
           // which is not a valid content type. Need to fix this.
-          response.setContentType(requiredType);
+          response.setHeader("Content-Type", requiredType);
         }
       } else {
-        response.setContentType(requiredType);
+        response.setHeader("Content-Type", requiredType);
       }
     }
   }

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=982090&r1=982089&r2=982090&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 Wed Aug  4 00:09:36 2010
@@ -42,6 +42,7 @@ import javax.servlet.ServletInputStream;
 import java.io.IOException;
 import java.util.Arrays;
 import java.util.Map;
+import java.util.Vector;
 
 import static org.easymock.EasyMock.capture;
 import static org.easymock.EasyMock.expect;
@@ -106,6 +107,8 @@ public class HtmlAccelServletTest extend
     String queryParams = (url == null ? "" : "url=" + url + "&container=accel"
                                              + "&gadget=test");
     expect(request.getQueryString()).andReturn(queryParams).anyTimes();
+    Vector<String> headerNames = new Vector<String>();
+    expect(request.getHeaderNames()).andReturn(headerNames.elements());
   }
 
   private void expectPostRequest(String extraPath, String url,
@@ -136,6 +139,8 @@ public class HtmlAccelServletTest extend
         });
     expect(inputStream.read((byte[]) EasyMock.anyObject()))
         .andReturn(-1);
+    Vector<String> headerNames = new Vector<String>();
+    expect(request.getHeaderNames()).andReturn(headerNames.elements());
   }
 
   @Test

Modified: shindig/trunk/java/gadgets/src/test/java/org/apache/shindig/gadgets/servlet/MakeRequestHandlerTest.java
URL: http://svn.apache.org/viewvc/shindig/trunk/java/gadgets/src/test/java/org/apache/shindig/gadgets/servlet/MakeRequestHandlerTest.java?rev=982090&r1=982089&r2=982090&view=diff
==============================================================================
--- shindig/trunk/java/gadgets/src/test/java/org/apache/shindig/gadgets/servlet/MakeRequestHandlerTest.java (original)
+++ shindig/trunk/java/gadgets/src/test/java/org/apache/shindig/gadgets/servlet/MakeRequestHandlerTest.java Wed Aug  4 00:09:36 2010
@@ -23,16 +23,20 @@ import static org.easymock.EasyMock.capt
 import static org.easymock.EasyMock.expect;
 
 import com.google.common.collect.Lists;
+import com.google.common.collect.Maps;
 
 import org.apache.shindig.auth.AuthInfo;
 import org.apache.shindig.auth.SecurityToken;
+import org.apache.shindig.common.servlet.HttpUtilTest;
 import org.apache.shindig.common.testing.FakeGadgetToken;
 import org.apache.shindig.common.uri.Uri;
+import org.apache.shindig.config.ContainerConfig;
 import org.apache.shindig.gadgets.AuthType;
 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.uri.UriCommon.Param;
 import org.easymock.Capture;
 import org.easymock.IAnswer;
 import org.json.JSONArray;
@@ -41,9 +45,11 @@ import org.json.JSONObject;
 import org.junit.Before;
 import org.junit.Test;
 
+import java.util.Arrays;
 import java.util.Collections;
 import java.util.Enumeration;
 import java.util.List;
+import java.util.Map;
 
 import javax.servlet.http.HttpServletRequest;
 
@@ -94,7 +100,7 @@ public class MakeRequestHandlerTest exte
   @Before
   public void setUp() {
     expect(request.getMethod()).andReturn("POST").anyTimes();
-    expect(request.getParameter(ProxyBase.URL_PARAM))
+    expect(request.getParameter(Param.URL.getKey()))
         .andReturn(REQUEST_URL.toString()).anyTimes();
   }
 
@@ -131,7 +137,7 @@ public class MakeRequestHandlerTest exte
 
   @Test
   public void testGetRequestWithRefresh() throws Exception {
-    expect(request.getParameter(ProxyBase.REFRESH_PARAM)).andReturn("120").anyTimes();
+    expect(request.getParameter(Param.REFRESH.getKey())).andReturn("120").anyTimes();
 
     Capture<HttpRequest> requestCapture = new Capture<HttpRequest>();
     expect(pipeline.execute(capture(requestCapture))).andReturn(new HttpResponse(RESPONSE_BODY));
@@ -147,14 +153,18 @@ public class MakeRequestHandlerTest exte
 
   @Test
   public void testGetRequestWithBadTtl() throws Exception {
-    expect(request.getParameter(ProxyBase.REFRESH_PARAM)).andReturn("foo").anyTimes();
+    expect(request.getParameter(Param.REFRESH.getKey())).andReturn("foo").anyTimes();
 
     Capture<HttpRequest> requestCapture = new Capture<HttpRequest>();
     expect(pipeline.execute(capture(requestCapture))).andReturn(new HttpResponse(RESPONSE_BODY));
 
     replay();
 
-    handler.fetch(request, recorder);
+    try {
+      handler.fetch(request, recorder);
+    } catch (GadgetException e) {
+      // Expected - catch now occurs at the MakeRequestServlet level.
+    }
 
     HttpRequest httpRequest = requestCapture.getValue();
     assertEquals(null, recorder.getHeader("Cache-Control"));
@@ -422,7 +432,7 @@ public class MakeRequestHandlerTest exte
         .andReturn(AuthType.SIGNED.toString()).atLeastOnce();
     replay();
 
-    handler.doFetch(request, recorder);
+    handler.fetch(request, recorder);
   }
 
   @Test
@@ -486,4 +496,114 @@ public class MakeRequestHandlerTest exte
     assertEquals(1, locations.length());
     assertEquals("somewhere else", locations.get(0));
   }
+
+  @Test
+  public void testSetResponseHeaders() throws Exception {
+    HttpResponse results = new HttpResponseBuilder().create();
+    replay();
+
+    MakeRequestHandler.setResponseHeaders(request, recorder, results);
+
+    // Just verify that they were set. Specific values are configurable.
+    assertNotNull("Expires header not set", recorder.getHeader("Expires"));
+    assertNotNull("Cache-Control header not set", recorder.getHeader("Cache-Control"));
+    assertEquals("attachment;filename=p.txt", recorder.getHeader("Content-Disposition"));
+  }
+
+  @Test
+  public void testSetContentTypeHeader() throws Exception {
+    HttpResponse results = new HttpResponseBuilder()
+        .create();
+    replay();
+    MakeRequestHandler.setResponseHeaders(request, recorder, results);
+
+    assertEquals("application/octet-stream", recorder.getHeader("Content-Type"));
+  }
+
+  @Test
+  public void testSetResponseHeadersNoCache() throws Exception {
+    Map<String, List<String>> headers = Maps.newTreeMap(String.CASE_INSENSITIVE_ORDER);
+    headers.put("Pragma", Arrays.asList("no-cache"));
+    HttpResponse results = new HttpResponseBuilder()
+        .addHeader("Pragma", "no-cache")
+        .create();
+    replay();
+
+    MakeRequestHandler.setResponseHeaders(request, recorder, results);
+
+    // Just verify that they were set. Specific values are configurable.
+    assertNotNull("Expires header not set", recorder.getHeader("Expires"));
+    assertEquals("no-cache", recorder.getHeader("Pragma"));
+    assertEquals("no-cache", recorder.getHeader("Cache-Control"));
+    assertEquals("attachment;filename=p.txt", recorder.getHeader("Content-Disposition"));
+  }
+
+  @Test
+  public void testSetResponseHeadersForceParam() throws Exception {
+    HttpResponse results = new HttpResponseBuilder().create();
+    expect(request.getParameter(Param.REFRESH.getKey())).andReturn("30").anyTimes();
+    replay();
+
+    MakeRequestHandler.setResponseHeaders(request, recorder, results);
+
+    HttpUtilTest.checkCacheControlHeaders(HttpUtilTest.testStartTime, recorder, 30, false);
+    assertEquals("attachment;filename=p.txt", recorder.getHeader("Content-Disposition"));
+  }
+
+  @Test
+  public void testSetResponseHeadersForceParamInvalid() throws Exception {
+    HttpResponse results = new HttpResponseBuilder().create();
+    expect(request.getParameter(Param.REFRESH.getKey())).andReturn("foo").anyTimes();
+    replay();
+
+    try {
+      MakeRequestHandler.setResponseHeaders(request, recorder, results);
+    } catch (GadgetException e) {
+      assertEquals(GadgetException.Code.INVALID_PARAMETER, e.getCode());
+    }
+  }
+
+  @Test
+  public void testGetParameter() {
+    expect(request.getParameter("foo")).andReturn("bar");
+    replay();
+
+    assertEquals("bar", MakeRequestHandler.getParameter(request, "foo", "not foo"));
+  }
+
+  @Test
+  public void testGetParameterWithNullValue() {
+    expect(request.getParameter("foo")).andReturn(null);
+    replay();
+
+    assertEquals("not foo", MakeRequestHandler.getParameter(request, "foo", "not foo"));
+  }
+
+  @Test
+  public void testGetContainerWithContainer() {
+    expect(request.getParameter(Param.CONTAINER.getKey())).andReturn("bar");
+    replay();
+
+    assertEquals("bar", MakeRequestHandler.getContainer(request));
+  }
+
+  @SuppressWarnings("deprecation")
+  @Test
+  public void testGetContainerWithSynd() {
+    expect(request.getParameter(Param.CONTAINER.getKey())).andReturn(null);
+    expect(request.getParameter(Param.SYND.getKey())).andReturn("syndtainer");
+    replay();
+
+    assertEquals("syndtainer", MakeRequestHandler.getContainer(request));
+  }
+
+  @SuppressWarnings("deprecation")
+  @Test
+  public void testGetContainerNoParam() {
+    expect(request.getParameter(Param.CONTAINER.getKey())).andReturn(null);
+    expect(request.getParameter(Param.SYND.getKey())).andReturn(null);
+    replay();
+
+    assertEquals(ContainerConfig.DEFAULT_CONTAINER, MakeRequestHandler.getContainer(request));
+  }
 }

Modified: shindig/trunk/java/gadgets/src/test/java/org/apache/shindig/gadgets/servlet/MakeRequestServletTest.java
URL: http://svn.apache.org/viewvc/shindig/trunk/java/gadgets/src/test/java/org/apache/shindig/gadgets/servlet/MakeRequestServletTest.java?rev=982090&r1=982089&r2=982090&view=diff
==============================================================================
--- shindig/trunk/java/gadgets/src/test/java/org/apache/shindig/gadgets/servlet/MakeRequestServletTest.java (original)
+++ shindig/trunk/java/gadgets/src/test/java/org/apache/shindig/gadgets/servlet/MakeRequestServletTest.java Wed Aug  4 00:09:36 2010
@@ -26,6 +26,7 @@ import org.apache.shindig.common.uri.Uri
 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.uri.UriCommon.Param;
 
 import org.json.JSONException;
 import org.json.JSONObject;
@@ -61,7 +62,7 @@ public class MakeRequestServletTest exte
     expect(request.getHeaderNames()).andReturn(EMPTY_ENUM).anyTimes();
     expect(request.getParameter(MakeRequestHandler.METHOD_PARAM))
         .andReturn("GET").anyTimes();
-    expect(request.getParameter(ProxyBase.URL_PARAM))
+    expect(request.getParameter(Param.URL.getKey()))
         .andReturn(REQUEST_URL.toString()).anyTimes();
   }
 

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=982090&r1=982089&r2=982090&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  4 00:09:36 2010
@@ -25,33 +25,52 @@ import static org.easymock.EasyMock.isA;
 import com.google.common.base.Objects;
 import com.google.common.collect.Maps;
 
-import org.apache.shindig.common.servlet.HttpServletResponseRecorder;
+import org.apache.shindig.common.EasyMockTestCase;
 import org.apache.shindig.common.uri.Uri;
-import org.apache.shindig.common.util.Utf8UrlCoder;
+import org.apache.shindig.common.uri.UriBuilder;
+import org.apache.shindig.config.ContainerConfig;
 import org.apache.shindig.gadgets.GadgetException;
+import org.apache.shindig.gadgets.LockedDomainService;
 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.CaptureRewriter;
+import org.apache.shindig.gadgets.rewrite.DefaultResponseRewriterRegistry;
+import org.apache.shindig.gadgets.rewrite.ResponseRewriter;
+import org.apache.shindig.gadgets.rewrite.ResponseRewriterRegistry;
 import org.apache.shindig.gadgets.uri.PassthruManager;
 import org.apache.shindig.gadgets.uri.ProxyUriManager;
 import org.apache.shindig.gadgets.uri.UriCommon.Param;
 import org.easymock.Capture;
+
+import org.junit.Before;
 import org.junit.Test;
 
 import java.util.Arrays;
 import java.util.List;
 import java.util.Map;
 
-import javax.servlet.http.HttpServletResponse;
-
-public class ProxyHandlerTest extends ServletTestFixture {
+public class ProxyHandlerTest extends EasyMockTestCase {
   private final static String URL_ONE = "http://www.example.org/test.html";
   private final static String DATA_ONE = "hello world";
 
   private final ProxyUriManager passthruManager = new PassthruManager();
+  public final LockedDomainService lockedDomainService = mock(LockedDomainService.class);
+  public final RequestPipeline pipeline = mock(RequestPipeline.class);
+  public CaptureRewriter rewriter = new CaptureRewriter();
+  public ResponseRewriterRegistry rewriterRegistry
+      = new DefaultResponseRewriterRegistry(Arrays.<ResponseRewriter>asList(rewriter), null);
+  private HttpRequest request;
+  
   private final ProxyHandler proxyHandler
-      = new ProxyHandler(pipeline, lockedDomainService, rewriterRegistry, passthruManager, true);
+      = new ProxyHandler(pipeline, lockedDomainService, rewriterRegistry, passthruManager);
 
+  @Before
+  public void setUp() {
+    request = new HttpRequest(Uri.parse(URL_ONE)); 
+  }
+  
   private void expectGetAndReturnData(String url, byte[] data) throws Exception {
     HttpRequest req = new HttpRequest(Uri.parse(url));
     HttpResponse resp = new HttpResponseBuilder().setResponse(data).create();
@@ -64,49 +83,31 @@ public class ProxyHandlerTest extends Se
     HttpResponse resp = new HttpResponseBuilder().addAllHeaders(headers).create();
     expect(pipeline.execute(req)).andReturn(resp);
   }
-
-  private void setupProxyRequestBase(String host) {
-    expect(request.getServerName()).andReturn(host).anyTimes();
-    expect(request.getScheme()).andReturn("http").anyTimes();
-    expect(request.getServerPort()).andReturn(80).anyTimes();
-    expect(request.getRequestURI()).andReturn("/").anyTimes();
+  
+  private UriBuilder setupProxyRequestBase(String host) {
+    UriBuilder builder = new UriBuilder().setScheme("http").setAuthority(host);
+    request.setHeader("Host", host);
+    return builder;
   }
 
-  private void setupProxyRequestMock(String host, String url, String... extraParams)
+  private void setupProxyRequestMock(String host, String url, String... params)
       throws Exception {
-    setupProxyRequestBase(host);
-    expect(request.getHeader("Host")).andReturn(host);
-    StringBuffer query = new StringBuffer("");
-    if (null != url) {
-      query.append("url=").append(Utf8UrlCoder.encode(url)).append('&');
+    UriBuilder builder = setupProxyRequestBase(host);
+    if (url != null) {
+      builder.addQueryParameter(Param.URL.getKey(), url);
     }
-    query.append("container=default");
-    String[] params = extraParams;
+    builder.addQueryParameter(Param.CONTAINER.getKey(), ContainerConfig.DEFAULT_CONTAINER);
     if (params != null && params.length > 0) {
       for (int i = 0; i < params.length; i += 2) {
-        query.append('&').append(params[i]).append('=').append(
-            Utf8UrlCoder.encode(params[i+1]));
+        builder.addQueryParameter(params[i], params[i+1]);
       }
     }
-    expect(request.getQueryString()).andReturn(query.toString());
+    request.setUri(builder.toUri());
   }
 
   private void setupFailedProxyRequestMock(String host, String url) throws Exception {
-    setupProxyRequestBase(host);
-    expect(request.getHeader("Host")).andReturn(host);
-  }
-
-  @Test
-  public void testIfModifiedSinceAlwaysReturnsEarly() throws Exception {
-    expect(request.getHeader("If-Modified-Since"))
-        .andReturn("Yes, this is an invalid header.");
-    replay();
-
-    proxyHandler.fetch(request, recorder);
-    verify();
-
-    assertEquals(HttpServletResponse.SC_NOT_MODIFIED, recorder.getHttpStatusCode());
-    assertFalse(rewriter.responseWasRewritten());
+    UriBuilder builder = setupProxyRequestBase(host);
+    request.setUri(builder.toUri());
   }
 
   @Test
@@ -114,12 +115,12 @@ public class ProxyHandlerTest extends Se
     setupProxyRequestMock("www.example.com", URL_ONE);
     expect(lockedDomainService.isSafeForOpenProxy("www.example.com")).andReturn(true);
     expectGetAndReturnData(URL_ONE, DATA_ONE.getBytes());
+   
     replay();
-
-    proxyHandler.fetch(request, recorder);
+    HttpResponse response = proxyHandler.fetch(request);
     verify();
 
-    assertEquals(DATA_ONE, recorder.getResponseAsString());
+    assertEquals(DATA_ONE, response.getResponseAsString());
     assertTrue(rewriter.responseWasRewritten());
   }
 
@@ -129,7 +130,7 @@ public class ProxyHandlerTest extends Se
     expect(lockedDomainService.isSafeForOpenProxy("www.example.com")).andReturn(true);
     replay();
 
-    proxyHandler.doFetch(request, recorder);
+    proxyHandler.fetch(request);
     fail("Proxy should raise exception if there is no url");
   }
 
@@ -144,14 +145,14 @@ public class ProxyHandlerTest extends Se
     expect(pipeline.execute(capture(httpRequest))).andReturn(resp);
 
     replay();
-    proxyHandler.fetch(request, recorder);
+    HttpResponse response = proxyHandler.fetch(request);
     verify();
 
     // Check that the HttpRequest passed in has all the relevant fields sets
     assertEquals("default", httpRequest.getValue().getContainer());
     assertEquals(Uri.parse(URL_ONE), httpRequest.getValue().getUri());
 
-    assertEquals(DATA_ONE, recorder.getResponseAsString());
+    assertEquals(DATA_ONE, response.getResponseAsString());
     assertTrue(rewriter.responseWasRewritten());
   }
 
@@ -161,7 +162,7 @@ public class ProxyHandlerTest extends Se
     expect(lockedDomainService.isSafeForOpenProxy("www.example.com")).andReturn(false);
     replay();
 
-    proxyHandler.doFetch(request, response);
+    proxyHandler.fetch(request);
   }
 
   @Test
@@ -171,36 +172,62 @@ public class ProxyHandlerTest extends Se
     String domain = "example.org";
     String contentType = "text/evil; charset=UTF-8";
     String magicGarbage = "fadfdfdfd";
-    final String badHeader = "Caching Server";
-    String badValue ="server";
     Map<String, List<String>> headers = Maps.newHashMap();
     headers.put("Content-Type", Arrays.asList(contentType));
     headers.put("X-Magic-Garbage", Arrays.asList(magicGarbage));
-    headers.put(badHeader, Arrays.asList(badValue));
 
     expect(lockedDomainService.isSafeForOpenProxy(domain)).andReturn(true).atLeastOnce();
     setupProxyRequestMock(domain, url);
     expectGetAndReturnHeaders(url, headers);
 
     replay();
+    HttpResponse response = proxyHandler.fetch(request);
+    verify();
 
-    HttpServletResponseRecorder newRecorder = new HttpServletResponseRecorder(response) {
-      @Override
-      public void addHeader(String name, String value) {
-        if (name.equals(badHeader)) {
-          throw new IllegalArgumentException("Bad header");
-        }
-        super.addHeader(name, value);
-      }
-    };
-    proxyHandler.fetch(request, newRecorder);
+    assertEquals(contentType, response.getHeader("Content-Type"));
+    assertEquals(magicGarbage, response.getHeader("X-Magic-Garbage"));
+    assertTrue(rewriter.responseWasRewritten());
+  }
+  
+  @Test
+  public void testOctetSetOnNullContentType() throws Exception {
+    String url = "http://example.org/file.evil";
+    String domain = "example.org";
+
+    expect(lockedDomainService.isSafeForOpenProxy(domain)).andReturn(true).atLeastOnce();
+    setupProxyRequestMock(domain, url);
+    expectGetAndReturnHeaders(url, Maps.<String, List<String>>newHashMap());
+
+    replay();
+    HttpResponse response = proxyHandler.fetch(request);
+    verify();
 
-    assertEquals(contentType, newRecorder.getHeader("Content-Type"));
-    assertEquals(magicGarbage, newRecorder.getHeader("X-Magic-Garbage"));
-    assertNull("Blocked header", newRecorder.getHeader(badHeader));
+    assertEquals("application/octet-stream", response.getHeader("Content-Type"));
+    assertNotNull(response.getHeader("Content-Disposition"));
     assertTrue(rewriter.responseWasRewritten());
   }
+  
+  @Test
+  public void testNoContentDispositionForFlash() throws Exception {
+    // Some headers may be blacklisted. These are OK.
+    String url = "http://example.org/file.evil";
+    String domain = "example.org";
+    Map<String, List<String>> headers = Maps.newHashMap();
+    headers.put("Content-Type", Arrays.asList("application/x-shockwave-flash"));
+
+    expect(lockedDomainService.isSafeForOpenProxy(domain)).andReturn(true).atLeastOnce();
+    setupProxyRequestMock(domain, url);
+    expectGetAndReturnHeaders(url, headers);
 
+    replay();
+    HttpResponse response = proxyHandler.fetch(request);
+    verify();
+
+    assertEquals("application/x-shockwave-flash", response.getHeader("Content-Type"));
+    assertNull(response.getHeader("Content-Disposition"));
+    assertTrue(rewriter.responseWasRewritten());
+  }
+  
   @Test
   public void testGetFallback() throws Exception {
     String url = "http://example.org/file.evil";
@@ -208,7 +235,7 @@ public class ProxyHandlerTest extends Se
     String fallback_url = "http://fallback.com/fallback.png";
 
     expect(lockedDomainService.isSafeForOpenProxy(domain)).andReturn(true).atLeastOnce();
-    setupProxyRequestMock(domain, url, ProxyBase.IGNORE_CACHE_PARAM, "1",
+    setupProxyRequestMock(domain, url, Param.NO_CACHE.getKey(), "1",
         Param.FALLBACK_URL_PARAM.getKey(), fallback_url);
 
     HttpRequest req = new HttpRequest(Uri.parse(url)).setIgnoreCache(true);
@@ -218,9 +245,7 @@ public class ProxyHandlerTest extends Se
     expect(pipeline.execute(isA(HttpRequest.class))).andReturn(fallback_resp);
 
     replay();
-
-    proxyHandler.fetch(request, recorder);
-
+    proxyHandler.fetch(request);
     verify();
   }
 
@@ -230,48 +255,17 @@ public class ProxyHandlerTest extends Se
     String domain = "example.org";
 
     expect(lockedDomainService.isSafeForOpenProxy(domain)).andReturn(true).atLeastOnce();
-    setupProxyRequestMock(domain, url, ProxyBase.IGNORE_CACHE_PARAM, "1");
+    setupProxyRequestMock(domain, url, Param.NO_CACHE.getKey(), "1");
 
     HttpRequest req = new HttpRequest(Uri.parse(url)).setIgnoreCache(true);
     HttpResponse resp = new HttpResponse("Hello");
     expect(pipeline.execute(req)).andReturn(resp);
 
     replay();
-
-    proxyHandler.fetch(request, recorder);
-
+    proxyHandler.fetch(request);
     verify();
   }
 
-  @Test
-  public void testInvalidHeaderDropped() throws Exception {
-    String url = "http://example.org/mypage.html";
-    String domain = "example.org";
-
-    expect(lockedDomainService.isSafeForOpenProxy(domain)).andReturn(true).atLeastOnce();
-    setupProxyRequestMock(domain, url);
-
-    HttpRequest req = new HttpRequest(Uri.parse(url));
-    String contentType = "text/html; charset=UTF-8";
-    HttpResponse resp = new HttpResponseBuilder()
-        .setResponseString("Hello")
-        .addHeader("Content-Type", contentType)
-        .addHeader("Content-Length", "200")  // Disallowed header.
-        .addHeader(":", "someDummyValue") // Invalid header name.
-        .create();
-
-    expect(pipeline.execute(req)).andReturn(resp);
-
-    replay();
-
-    proxyHandler.fetch(request, recorder);
-
-    verify();
-    assertNull(recorder.getHeader(":"));
-    assertNull(recorder.getHeader("Content-Length"));
-    assertEquals(recorder.getHeader("Content-Type"), contentType);
-  }
-  
   /**
    * Override HttpRequest equals to check for cache control fields
    */
@@ -305,16 +299,14 @@ public class ProxyHandlerTest extends Se
     String domain = "example.org";
 
     expect(lockedDomainService.isSafeForOpenProxy(domain)).andReturn(true).atLeastOnce();
-    setupProxyRequestMock(domain, url, ProxyBase.REFRESH_PARAM, "120");
+    setupProxyRequestMock(domain, url, Param.REFRESH.getKey(), "120");
 
     HttpRequest req = new HttpRequestCache(Uri.parse(url)).setCacheTtl(120).setIgnoreCache(false);
     HttpResponse resp = new HttpResponse("Hello");
     expect(pipeline.execute(req)).andReturn(resp);
 
     replay();
-
-    proxyHandler.fetch(request, recorder);
-
+    proxyHandler.fetch(request);
     verify();
   }
 
@@ -324,16 +316,14 @@ public class ProxyHandlerTest extends Se
     String domain = "example.org";
 
     expect(lockedDomainService.isSafeForOpenProxy(domain)).andReturn(true).atLeastOnce();
-    setupProxyRequestMock(domain, url, ProxyBase.REFRESH_PARAM, "foo");
+    setupProxyRequestMock(domain, url, Param.REFRESH.getKey(), "foo");
 
     HttpRequest req = new HttpRequestCache(Uri.parse(url)).setCacheTtl(-1).setIgnoreCache(false);
     HttpResponse resp = new HttpResponse("Hello");
     expect(pipeline.execute(req)).andReturn(resp);
 
     replay();
-
-    proxyHandler.fetch(request, recorder);
-
+    proxyHandler.fetch(request);
     verify();
   }
 
@@ -343,31 +333,29 @@ public class ProxyHandlerTest extends Se
     String domain = "example.org";
 
     expect(lockedDomainService.isSafeForOpenProxy(domain)).andReturn(true).atLeastOnce();
-    expect(request.getRemoteAddr()).andReturn("127.0.0.1").atLeastOnce();
+    request.setHeader("X-Forwarded-For", "127.0.0.1");
     setupProxyRequestMock(domain, url);
 
     HttpRequest req = new HttpRequest(Uri.parse(url));
-    req.setHeader("X-Forwarded-For","127.0.0.1");
+    req.setHeader("X-Forwarded-For", "127.0.0.1");
 
     HttpResponse resp = new HttpResponse("Hello");
 
     expect(pipeline.execute(req)).andReturn(resp);
 
     replay();
-
-    proxyHandler.fetch(request, recorder);
-
+    proxyHandler.fetch(request);
     verify();
   }
 
   private void expectMime(String expectedMime, String contentMime, String outputMime)
       throws Exception {
-    String url = "http://example.org/file.img?" + ProxyHandler.REWRITE_MIME_TYPE_PARAM +
+    String url = "http://example.org/file.img?" + Param.REWRITE_MIME_TYPE.getKey() +
         '=' + expectedMime;
     String domain = "example.org";
 
     expect(lockedDomainService.isSafeForOpenProxy(domain)).andReturn(true).atLeastOnce();
-    setupProxyRequestMock(domain, url, ProxyHandler.REWRITE_MIME_TYPE_PARAM, expectedMime);
+    setupProxyRequestMock(domain, url, Param.REWRITE_MIME_TYPE.getKey(), expectedMime);
 
     HttpRequest req = new HttpRequest(Uri.parse(url))
         .setRewriteMimeType(expectedMime);
@@ -378,26 +366,28 @@ public class ProxyHandlerTest extends Se
       .create();
 
     expect(pipeline.execute(req)).andReturn(resp);
+    
     replay();
-    proxyHandler.fetch(request, recorder);
+    HttpResponse response = proxyHandler.fetch(request);
     verify();
-    assertEquals(recorder.getContentType(), outputMime);
+    
+    assertEquals(outputMime, response.getHeader("Content-Type"));
     reset();
   }
 
   @Test
   public void testMimeMatchPass() throws Exception {
-    expectMime("text/css", "text/css", "text/css");
+    expectMime("text/css", "text/css", "text/css; charset=UTF-8");
   }
 
   @Test
   public void testMimeMatchPassWithAdditionalAttributes() throws Exception {
-    expectMime("text/css", "text/css; charset=UTF-8", "text/css");
+    expectMime("text/css", "text/css", "text/css; charset=UTF-8");
   }
 
   @Test
   public void testMimeMatchOverrideNonMatch() throws Exception {
-    expectMime("text/css", "image/png; charset=UTF-8", "text/css");
+    expectMime("text/css", "image/png", "text/css; charset=UTF-8");
   }
 
   @Test

Modified: shindig/trunk/java/gadgets/src/test/java/org/apache/shindig/gadgets/servlet/ProxyServletTest.java
URL: http://svn.apache.org/viewvc/shindig/trunk/java/gadgets/src/test/java/org/apache/shindig/gadgets/servlet/ProxyServletTest.java?rev=982090&r1=982089&r2=982090&view=diff
==============================================================================
--- shindig/trunk/java/gadgets/src/test/java/org/apache/shindig/gadgets/servlet/ProxyServletTest.java (original)
+++ shindig/trunk/java/gadgets/src/test/java/org/apache/shindig/gadgets/servlet/ProxyServletTest.java Wed Aug  4 00:09:36 2010
@@ -1,5 +1,5 @@
 /*
- * Licensed to the Apache Software Foundation (ASF) under one
+pro * Licensed to the Apache Software Foundation (ASF) under one
  * or more contributor license agreements.  See the NOTICE file
  * distributed with this work for additional information
  * regarding copyright ownership.  The ASF licenses this file
@@ -19,19 +19,22 @@
 package org.apache.shindig.gadgets.servlet;
 
 import static junitx.framework.StringAssert.assertContains;
+import static org.easymock.EasyMock.capture;
 import static org.easymock.EasyMock.expect;
 
 import org.apache.shindig.common.uri.Uri;
 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.uri.PassthruManager;
-import org.apache.shindig.gadgets.uri.ProxyUriManager;
+import org.apache.shindig.gadgets.uri.UriCommon.Param;
+import org.easymock.Capture;
 import org.junit.Before;
 import org.junit.Test;
 
 import javax.servlet.http.HttpServletResponse;
 
+import java.util.Vector;
+
 /**
  * Tests for ProxyServlet.
  *
@@ -45,21 +48,15 @@ public class ProxyServletTest extends Se
   private static final String RESPONSE_BODY = "Hello, world!";
   private static final String ERROR_MESSAGE = "Broken!";
 
-  private final ProxyUriManager passthruManager = new PassthruManager();
-  private final ProxyHandler proxyHandler
-      = new ProxyHandler(pipeline, lockedDomainService, null, passthruManager, true);
+  private final ProxyHandler proxyHandler = mock(ProxyHandler.class);
   private final ProxyServlet servlet = new ProxyServlet();
-  private final HttpRequest internalRequest = new HttpRequest(REQUEST_URL);
-  private final HttpResponse internalResponse = new HttpResponse(RESPONSE_BODY);
 
   @Before
   public void setUp() throws Exception {
     servlet.setProxyHandler(proxyHandler);
-    expect(request.getParameter(ProxyBase.URL_PARAM))
+    expect(request.getParameter(Param.URL.getKey()))
         .andReturn(REQUEST_URL.toString()).anyTimes();
     expect(request.getHeader("Host")).andReturn(REQUEST_DOMAIN).anyTimes();
-    expect(lockedDomainService.isSafeForOpenProxy(REQUEST_DOMAIN))
-        .andReturn(true).anyTimes();
   }
   
   private void setupRequest(String str) {
@@ -69,6 +66,8 @@ public class ProxyServletTest extends Se
     expect(request.getServerPort()).andReturn(80);
     expect(request.getRequestURI()).andReturn(uri.getPath());
     expect(request.getQueryString()).andReturn(uri.getQuery());
+    Vector<String> headerNames = new Vector<String>();
+    expect(request.getHeaderNames()).andReturn(headerNames.elements());
   }
 
   private void assertResponseOk(int expectedStatus, String expectedBody) {
@@ -77,37 +76,58 @@ public class ProxyServletTest extends Se
   }
 
   @Test
+  public void testIfModifiedSinceAlwaysReturnsEarly() throws Exception {
+    expect(request.getHeader("If-Modified-Since")).andReturn("Yes, this is an invalid header");
+   
+    replay();
+    servlet.doGet(request, recorder);
+    verify();
+
+    assertEquals(HttpServletResponse.SC_NOT_MODIFIED, recorder.getHttpStatusCode());
+    assertFalse(rewriter.responseWasRewritten());
+  }
+
+  @Test
   public void testDoGetNormal() throws Exception {
     setupRequest(BASIC_SYNTAX_URL);
-    expect(pipeline.execute(internalRequest)).andReturn(internalResponse);
+    Capture<HttpRequest> requestCapture = new Capture<HttpRequest>();
+    expect(proxyHandler.fetch(capture(requestCapture))).andReturn(new HttpResponse(RESPONSE_BODY));
+    
     replay();
-
     servlet.doGet(request, recorder);
+    verify();
 
     assertResponseOk(HttpResponse.SC_OK, RESPONSE_BODY);
+    assertEquals(BASIC_SYNTAX_URL, requestCapture.getValue().getUri().toString());
   }
 
   @Test
   public void testDoGetHttpError() throws Exception {
     setupRequest(BASIC_SYNTAX_URL);
-    expect(pipeline.execute(internalRequest)).andReturn(HttpResponse.notFound());
+    Capture<HttpRequest> requestCapture = new Capture<HttpRequest>();
+    expect(proxyHandler.fetch(capture(requestCapture))).andReturn(HttpResponse.notFound());
+    
     replay();
-
     servlet.doGet(request, recorder);
+    verify();
 
     assertResponseOk(HttpResponse.SC_NOT_FOUND, "");
+    assertEquals(BASIC_SYNTAX_URL, requestCapture.getValue().getUri().toString());
   }
 
   @Test
   public void testDoGetException() throws Exception {
     setupRequest(BASIC_SYNTAX_URL);
-    expect(pipeline.execute(internalRequest)).andThrow(
+    Capture<HttpRequest> requestCapture = new Capture<HttpRequest>();
+    expect(proxyHandler.fetch(capture(requestCapture))).andThrow(
         new GadgetException(GadgetException.Code.FAILED_TO_RETRIEVE_CONTENT, ERROR_MESSAGE));
+   
     replay();
-
     servlet.doGet(request, recorder);
+    verify();
 
     assertEquals(HttpServletResponse.SC_BAD_REQUEST, recorder.getHttpStatusCode());
     assertContains(ERROR_MESSAGE, recorder.getResponseAsString());
+    assertEquals(BASIC_SYNTAX_URL, requestCapture.getValue().getUri().toString());
   }
 }