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());
}
}