You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@shindig.apache.org by ch...@apache.org on 2010/08/07 02:17:49 UTC

svn commit: r983148 - in /shindig/trunk/java/gadgets/src: main/java/org/apache/shindig/gadgets/http/ main/java/org/apache/shindig/gadgets/render/ main/java/org/apache/shindig/gadgets/rewrite/ main/java/org/apache/shindig/gadgets/uri/ test/java/org/apac...

Author: chirag
Date: Sat Aug  7 00:17:49 2010
New Revision: 983148

URL: http://svn.apache.org/viewvc?rev=983148&view=rev
Log:
Cajole proxied javascript responses with CajaResponseRewriter
Code Review: http://codereview.appspot.com/1847052/show

Added:
    shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/render/CajaResponseRewriter.java
    shindig/trunk/java/gadgets/src/test/java/org/apache/shindig/gadgets/render/CajaResponseRewriterTest.java
Modified:
    shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/http/HttpRequest.java
    shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/rewrite/RewriteModule.java
    shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/uri/ProxyUriBase.java
    shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/uri/UriCommon.java

Modified: shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/http/HttpRequest.java
URL: http://svn.apache.org/viewvc/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/http/HttpRequest.java?rev=983148&r1=983147&r2=983148&view=diff
==============================================================================
--- shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/http/HttpRequest.java (original)
+++ shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/http/HttpRequest.java Sat Aug  7 00:17:49 2010
@@ -69,6 +69,9 @@ public class HttpRequest {
   // Sanitization
   private boolean sanitizationRequested;
 
+  // Caja
+  private boolean cajaRequested;
+
   // Whether to follow redirects
   private boolean followRedirects = true;
 
@@ -220,6 +223,18 @@ public class HttpRequest {
     this.sanitizationRequested = sanitizationRequested;
   }
 
+    /**
+   * Should content fetched in response to this request
+   * be sanitized based on the specified mime-type
+   */
+  public boolean isCajaRequested() {
+    return cajaRequested;
+  }
+
+  public void setCajaRequested(boolean cajaRequested) {
+    this.cajaRequested = cajaRequested;
+  }
+
   /**
    * @param cacheTtl The amount of time to cache the result object for, in seconds. If set to -1,
    * HTTP cache control headers will be honored. Otherwise objects will be cached for the time

Added: shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/render/CajaResponseRewriter.java
URL: http://svn.apache.org/viewvc/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/render/CajaResponseRewriter.java?rev=983148&view=auto
==============================================================================
--- shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/render/CajaResponseRewriter.java (added)
+++ shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/render/CajaResponseRewriter.java Sat Aug  7 00:17:49 2010
@@ -0,0 +1,197 @@
+/*
+ * 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.render;
+
+import com.google.caja.lexer.CharProducer;
+import com.google.caja.lexer.ExternalReference;
+import com.google.caja.lexer.FetchedData;
+import com.google.caja.lexer.InputSource;
+import com.google.caja.lexer.JsLexer;
+import com.google.caja.lexer.JsTokenQueue;
+import com.google.caja.lexer.ParseException;
+import com.google.caja.lexer.TokenConsumer;
+
+import com.google.caja.parser.AncestorChain;
+import com.google.caja.parser.ParseTreeNode;
+import com.google.caja.parser.js.CajoledModule;
+import com.google.caja.parser.js.Parser;
+import com.google.caja.plugin.PipelineMaker;
+import com.google.caja.plugin.PluginCompiler;
+import com.google.caja.plugin.PluginMeta;
+import com.google.caja.plugin.UriFetcher;
+import com.google.caja.plugin.UriPolicy;
+import com.google.caja.render.Concatenator;
+import com.google.caja.render.JsMinimalPrinter;
+import com.google.caja.render.JsPrettyPrinter;
+import com.google.caja.reporting.BuildInfo;
+import com.google.caja.reporting.MessageContext;
+import com.google.caja.reporting.MessageQueue;
+import com.google.caja.reporting.RenderContext;
+import com.google.caja.reporting.SimpleMessageQueue;
+import com.google.inject.Inject;
+
+import org.apache.commons.io.IOUtils;
+import org.apache.shindig.common.uri.Uri;
+import org.apache.shindig.gadgets.Gadget;
+import org.apache.shindig.gadgets.GadgetException;
+import org.apache.shindig.gadgets.http.HttpRequest;
+import org.apache.shindig.gadgets.http.HttpResponse;
+import org.apache.shindig.gadgets.http.HttpResponseBuilder;
+import org.apache.shindig.gadgets.http.RequestPipeline;
+import org.apache.shindig.gadgets.rewrite.DomWalker;
+import org.apache.shindig.gadgets.rewrite.ResponseRewriter;
+import org.apache.shindig.gadgets.rewrite.RewriterUtils;
+import org.apache.shindig.gadgets.rewrite.RewritingException;
+import org.apache.shindig.gadgets.uri.ProxyUriManager;
+
+import java.io.IOException;
+import java.io.StringReader;
+import java.util.Map;
+import java.util.logging.Logger;
+
+/**
+ * Rewriter that cajoles Javascript.
+ */
+public class CajaResponseRewriter implements ResponseRewriter {
+  private static final Logger LOG = Logger.getLogger(CajaResponseRewriter.class.getName());
+
+  private final RequestPipeline requestPipeline;
+
+  @Inject
+  public CajaResponseRewriter(RequestPipeline requestPipeline) {
+    this.requestPipeline = requestPipeline;
+  }
+
+  public void rewrite(HttpRequest req, HttpResponseBuilder resp) throws RewritingException {
+    if (!req.isCajaRequested()) { return; }
+
+    // Only accept Javascript for now
+    if (!RewriterUtils.isJavascript(req, resp)) {
+      resp.setContent("");
+      resp.setHttpStatusCode(HttpResponse.SC_BAD_REQUEST);
+      return;
+    }
+    
+    boolean passed = false;
+
+    MessageQueue mq = new SimpleMessageQueue();
+    MessageContext mc = new MessageContext();
+    Uri contextUri = req.getUri();
+    InputSource is = new InputSource(contextUri.toJavaUri());
+
+    PluginMeta pluginMeta = new PluginMeta(
+            proxyFetcher(req, contextUri), proxyUriPolicy(contextUri));
+    PluginCompiler compiler = new PluginCompiler(BuildInfo.getInstance(),
+            pluginMeta, mq);
+    compiler.setMessageContext(mc);
+
+    // Parse the javascript
+    try {
+      StringReader strReader = new StringReader(resp.getContent());
+      CharProducer cp = CharProducer.Factory.create(strReader, is);
+      JsTokenQueue tq = new JsTokenQueue(new JsLexer(cp), is);
+      ParseTreeNode input = new Parser(tq, mq).parse();
+      tq.expectEmpty();
+
+      compiler.addInput(AncestorChain.instance(input), contextUri.toJavaUri());
+    } catch (ParseException e) {
+      // Don't bother continuing.
+      resp.setContent("");
+      return;
+    }
+
+    try { 
+      if (RewriterUtils.isJavascript(req, resp)) {
+        compiler.setGoals(
+            compiler.getGoals().without(PipelineMaker.HTML_SAFE_STATIC));
+      }
+      passed = compiler.run();
+
+      CajoledModule outputJs = passed ? compiler.getJavascript() : null;
+
+      StringBuilder jsOut = new StringBuilder();
+      TokenConsumer printer;
+      if ("1".equals(req.getParam("debug"))) {
+        printer = new JsPrettyPrinter(new Concatenator(jsOut));
+      } else {
+        printer = new JsMinimalPrinter(new Concatenator(jsOut));
+      }
+
+      RenderContext renderContext = new RenderContext(printer).withEmbeddable(true);
+
+      if (outputJs != null) {
+        outputJs.render(renderContext);
+      }
+
+      renderContext.getOut().noMoreTokens();
+      resp.setContent(jsOut.toString());
+    } finally {
+      if (!passed) {
+        resp.setContent("");
+      }
+    }
+  }
+
+  private UriPolicy proxyUriPolicy(final Uri contextUri) {
+    final Gadget stubGadget = DomWalker.makeGadget(contextUri);
+
+    return new UriPolicy() {
+      public String rewriteUri(ExternalReference ref, UriEffect effect,
+          LoaderType loader, Map<String, ?> hints) {
+
+        Uri resourceUri = Uri.fromJavaUri(ref.getUri());
+        if (contextUri != null) {
+          resourceUri = contextUri.resolve(resourceUri);
+        }
+
+        ProxyUriManager.ProxyUri proxyUri = new ProxyUriManager.ProxyUri(
+            stubGadget, resourceUri);
+        return proxyUri.getResource().toString();
+      }
+    };
+  }
+
+  private UriFetcher proxyFetcher(final HttpRequest req, final Uri contextUri) {
+    return new UriFetcher() {
+      public FetchedData fetch(ExternalReference ref, String mimeType) throws UriFetchException {
+        Uri resourceUri = Uri.fromJavaUri(ref.getUri());
+        if (contextUri != null) {
+          resourceUri = contextUri.resolve(resourceUri);
+        }
+
+        HttpRequest request = new HttpRequest(resourceUri)
+                .setContainer(req.getContainer())
+                .setGadget(req.getGadget());
+        
+        try {
+          HttpResponse response = requestPipeline.execute(request);
+          byte[] responseBytes = IOUtils.toByteArray(response.getResponse());
+          return FetchedData.fromBytes(responseBytes, mimeType, response.getEncoding(),
+              new InputSource(ref.getUri()));
+        } catch (GadgetException e) {
+          LOG.info("Failed to retrieve: " + ref.toString());
+          return null;
+        } catch (IOException e) {
+          LOG.info("Failed to read: " + ref.toString());
+          return null;
+        }
+      }
+    };
+  }
+}

Modified: shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/rewrite/RewriteModule.java
URL: http://svn.apache.org/viewvc/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/rewrite/RewriteModule.java?rev=983148&r1=983147&r2=983148&view=diff
==============================================================================
--- shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/rewrite/RewriteModule.java (original)
+++ shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/rewrite/RewriteModule.java Sat Aug  7 00:17:49 2010
@@ -25,6 +25,7 @@ import com.google.inject.Singleton;
 import com.google.inject.name.Named;
 import com.google.inject.name.Names;
 import org.apache.shindig.gadgets.parse.GadgetHtmlParser;
+import org.apache.shindig.gadgets.render.CajaResponseRewriter;
 import org.apache.shindig.gadgets.render.OpenSocialI18NGadgetRewriter;
 import org.apache.shindig.gadgets.render.RenderingGadgetRewriter;
 import org.apache.shindig.gadgets.render.SanitizingGadgetRewriter;
@@ -101,10 +102,11 @@ public class RewriteModule extends Abstr
       StyleAdjacencyContentRewriter styleAdjacencyRewriter,
       ProxyingContentRewriter proxyingRewriter,
       CssResponseRewriter cssRewriter,
-      SanitizingResponseRewriter sanitizedRewriter) {
+      SanitizingResponseRewriter sanitizedRewriter,
+      CajaResponseRewriter cajaRewriter) {
     return ImmutableList.of(
         absolutePathRewriter, styleTagExtractorRewriter, styleAdjacencyRewriter, proxyingRewriter,
-        cssRewriter, sanitizedRewriter);
+        cssRewriter, sanitizedRewriter, cajaRewriter);
   }
 
   @Provides
@@ -115,7 +117,7 @@ public class RewriteModule extends Abstr
       StyleTagProxyEmbeddedUrlsRewriter styleTagProxyEmbeddedUrlsRewriter,
       ProxyingContentRewriter proxyingContentRewriter) {
     return ImmutableList.of((ResponseRewriter) absolutePathReferenceRewriter,
-                            (ResponseRewriter) styleTagProxyEmbeddedUrlsRewriter,
-                            (ResponseRewriter) proxyingContentRewriter);
+        (ResponseRewriter) styleTagProxyEmbeddedUrlsRewriter,
+        (ResponseRewriter) proxyingContentRewriter);
   }
 }

Modified: shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/uri/ProxyUriBase.java
URL: http://svn.apache.org/viewvc/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/uri/ProxyUriBase.java?rev=983148&r1=983147&r2=983148&view=diff
==============================================================================
--- shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/uri/ProxyUriBase.java (original)
+++ shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/uri/ProxyUriBase.java Sat Aug  7 00:17:49 2010
@@ -39,6 +39,7 @@ public class ProxyUriBase {
   private String gadget = null;
   private String rewriteMimeType = null;
   private boolean sanitizeContent = false;
+  private boolean cajoleContent = false;
   
   protected ProxyUriBase(Gadget gadget) {
     this(null,  // Meaningless in "context" mode. translateStatusRefresh invalid here.
@@ -83,6 +84,7 @@ public class ProxyUriBase {
       gadget = uri.getQueryParameter(Param.GADGET.getKey());
       rewriteMimeType = uri.getQueryParameter(Param.REWRITE_MIME_TYPE.getKey());
       sanitizeContent = getBooleanValue(uri.getQueryParameter(Param.SANITIZE.getKey()));
+      cajoleContent = getBooleanValue(uri.getQueryParameter(Param.CAJOLE.getKey()));      
     }  
   }
 
@@ -102,13 +104,15 @@ public class ProxyUriBase {
         && Objects.equal(this.rewriteMimeType, objUri.rewriteMimeType)
         && this.noCache == objUri.noCache
         && this.debug == objUri.debug
-        && this.sanitizeContent == objUri.sanitizeContent);
+        && this.sanitizeContent == objUri.sanitizeContent
+        && this.cajoleContent == objUri.cajoleContent);
+
   }
 
   @Override
   public int hashCode() {
     return Objects.hashCode(status, refresh, container, gadget, rewriteMimeType,
-            noCache, debug, sanitizeContent);
+            noCache, debug, sanitizeContent, cajoleContent);
   }
 
   public ProxyUriBase setRewriteMimeType(String type) {
@@ -120,6 +124,11 @@ public class ProxyUriBase {
     this.sanitizeContent = sanitize;
     return this;
   }
+
+  public ProxyUriBase setCajoleContent(boolean cajole) {
+    this.cajoleContent = cajole;
+    return this;
+  }
   
   public UriStatus getStatus() {
     return status;
@@ -153,6 +162,10 @@ public class ProxyUriBase {
     return sanitizeContent;
   }
 
+  public boolean cajoleContent() {
+    return cajoleContent;
+  }
+
   public HttpRequest makeHttpRequest(Uri targetUri) throws GadgetException {
     HttpRequest req = new HttpRequest(targetUri)
         .setIgnoreCache(isNoCache())
@@ -177,6 +190,7 @@ public class ProxyUriBase {
       req.setRewriteMimeType(getRewriteMimeType());
     }
     req.setSanitizationRequested(sanitizeContent());
+    req.setCajaRequested(cajoleContent());    
 
     return req;
   }
@@ -213,6 +227,9 @@ public class ProxyUriBase {
     if (sanitizeContent) {
       queryBuilder.addQueryParameter(Param.SANITIZE.getKey(), "1");
     }
+    if (cajoleContent) {
+      queryBuilder.addQueryParameter(Param.CAJOLE.getKey(), "1");
+    }
     return queryBuilder;
   }
 

Modified: shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/uri/UriCommon.java
URL: http://svn.apache.org/viewvc/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/uri/UriCommon.java?rev=983148&r1=983147&r2=983148&view=diff
==============================================================================
--- shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/uri/UriCommon.java (original)
+++ shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/uri/UriCommon.java Sat Aug  7 00:17:49 2010
@@ -39,6 +39,7 @@ public interface UriCommon {
     TYPE("type"),
     REWRITE_MIME_TYPE("rewriteMime"),
     SANITIZE("sanitize"),
+    CAJOLE("cajole"),    
     CONTAINER_MODE("c"),
     
     // Proxy resize params:

Added: shindig/trunk/java/gadgets/src/test/java/org/apache/shindig/gadgets/render/CajaResponseRewriterTest.java
URL: http://svn.apache.org/viewvc/shindig/trunk/java/gadgets/src/test/java/org/apache/shindig/gadgets/render/CajaResponseRewriterTest.java?rev=983148&view=auto
==============================================================================
--- shindig/trunk/java/gadgets/src/test/java/org/apache/shindig/gadgets/render/CajaResponseRewriterTest.java (added)
+++ shindig/trunk/java/gadgets/src/test/java/org/apache/shindig/gadgets/render/CajaResponseRewriterTest.java Sat Aug  7 00:17:49 2010
@@ -0,0 +1,84 @@
+/*
+ * 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.render;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+
+import org.apache.shindig.common.uri.Uri;
+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.RewriterTestBase;
+import org.apache.shindig.gadgets.rewrite.ResponseRewriter;
+import org.junit.Test;
+
+public class CajaResponseRewriterTest extends RewriterTestBase {
+  private static final Uri CONTENT_URI = Uri.parse("http://www.example.org/content");
+
+  private String rewrite(HttpRequest request, HttpResponse response) throws Exception {
+    request.setSanitizationRequested(true);
+    ResponseRewriter rewriter = createRewriter();
+
+    HttpResponseBuilder hrb = new HttpResponseBuilder(parser, response);
+    rewriter.rewrite(request, hrb);
+    return hrb.getContent();
+  }
+
+  private ResponseRewriter createRewriter() {
+    return new CajaResponseRewriter(new RequestPipeline() {
+      public HttpResponse execute(HttpRequest request) {
+        return null;
+      }
+    });
+  }
+
+  @Test
+  public void testJs() throws Exception {
+    HttpRequest req = new HttpRequest(CONTENT_URI);
+    req.setRewriteMimeType("text/javascript");
+    req.setCajaRequested(true);
+    HttpResponse response = new HttpResponseBuilder().setResponseString("var a;").create();
+    String sanitized = "$v.initOuter('a');";
+
+    assertTrue(rewrite(req, response).contains(sanitized));
+  }
+
+  @Test
+  public void testJsWithoutCaja() throws Exception {
+    HttpRequest req = new HttpRequest(CONTENT_URI);
+    req.setRewriteMimeType("text/javascript");
+    req.setCajaRequested(false);
+    HttpResponse response = new HttpResponseBuilder().setResponseString("var a;").create();
+    String sanitized = "var a;";
+
+    assertTrue(rewrite(req, response).contains(sanitized));
+  }
+
+  @Test
+  public void testNonJs() throws Exception {
+    HttpRequest req = new HttpRequest(CONTENT_URI);
+    req.setRewriteMimeType("text/html");
+    req.setCajaRequested(true);
+    HttpResponse response = new HttpResponseBuilder().setResponseString("<html></html>").create();
+
+    assertEquals("", rewrite(req, response));    
+  }
+}