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/03/08 23:59:42 UTC

svn commit: r920559 - in /shindig/trunk/java/gadgets/src: main/java/org/apache/shindig/gadgets/rewrite/CssRequestRewriter.java test/java/org/apache/shindig/gadgets/rewrite/CssRequestRewriterTest.java

Author: johnh
Date: Mon Mar  8 22:59:42 2010
New Revision: 920559

URL: http://svn.apache.org/viewvc?rev=920559&view=rev
Log:
Re-add CssRequestRewriter, with a version that uses ProxyUriManager rather than LinkRewriter instances.


Added:
    shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/rewrite/CssRequestRewriter.java
    shindig/trunk/java/gadgets/src/test/java/org/apache/shindig/gadgets/rewrite/CssRequestRewriterTest.java

Added: shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/rewrite/CssRequestRewriter.java
URL: http://svn.apache.org/viewvc/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/rewrite/CssRequestRewriter.java?rev=920559&view=auto
==============================================================================
--- shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/rewrite/CssRequestRewriter.java (added)
+++ shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/rewrite/CssRequestRewriter.java Mon Mar  8 22:59:42 2010
@@ -0,0 +1,219 @@
+/*
+ * 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.rewrite;
+
+import org.apache.commons.io.IOUtils;
+import org.apache.commons.lang.StringUtils;
+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.parse.caja.CajaCssLexerParser;
+import org.apache.shindig.gadgets.uri.ProxyUriManager;
+import org.w3c.dom.Element;
+
+import java.io.IOException;
+import java.io.Reader;
+import java.io.StringReader;
+import java.io.StringWriter;
+import java.io.Writer;
+import java.util.Collections;
+import java.util.List;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+import com.google.caja.lexer.ParseException;
+import com.google.common.collect.Lists;
+import com.google.inject.Inject;
+
+/**
+ * Rewrite links to referenced content in a stylesheet
+ */
+public class CssRequestRewriter implements RequestRewriter {
+
+  private static final Logger logger = Logger.getLogger(CssRequestRewriter.class.getName());
+
+  private final CajaCssLexerParser cssParser;
+  private final ProxyUriManager proxyUriManager;
+  private final ContentRewriterFeature.Factory rewriterFeatureFactory;
+
+  @Inject
+  public CssRequestRewriter(CajaCssLexerParser cssParser,
+      ProxyUriManager proxyUriManager, ContentRewriterFeature.Factory rewriterFeatureFactory) {
+    this.cssParser = cssParser;
+    this.proxyUriManager = proxyUriManager;
+    this.rewriterFeatureFactory = rewriterFeatureFactory;
+  }
+
+  public boolean rewrite(HttpRequest request, HttpResponse original,
+      MutableContent content) throws RewritingException {
+    ContentRewriterFeature.Config config = rewriterFeatureFactory.get(request);
+    if (!RewriterUtils.isCss(request, original)) {
+      return false;
+    }
+    
+    String css = content.getContent();
+    StringWriter sw = new StringWriter((css.length() * 110) / 100);
+    rewrite(new StringReader(css), request.getUri(),
+        new UriMaker(proxyUriManager, config), sw, false);
+    content.setContent(sw.toString());
+
+    return true;
+  }
+
+  /**
+   * Rewrite the given CSS content and optionally extract the import references.
+   * @param content CSS content
+   * @param source Uri of content
+   * @param rewriter Rewrite urls
+   * @param writer Output
+   * @param extractImports If true remove the import statements from the output and return their
+   *            referenced URIs.
+   * @return Empty list of extracted import URIs.
+   */
+  public List<String> rewrite(Reader content, Uri source,
+      UriMaker uriMaker, Writer writer, boolean extractImports) throws RewritingException {
+    try {
+      String original = IOUtils.toString(content);
+      try {
+        List<Object> stylesheet = cssParser.parse(original);
+        List<String> stringList = rewrite(stylesheet, source, uriMaker, extractImports);
+        // Serialize the stylesheet
+        cssParser.serialize(stylesheet, writer);
+        return stringList;
+      } catch (GadgetException ge) {
+        if (ge.getCause() instanceof ParseException) {
+          logger.log(Level.WARNING,
+              "Caja CSS parse failure: " + ge.getCause().getMessage() + " for " + source);
+          writer.write(original);
+          return Collections.emptyList();
+        } else {
+          throw new RewritingException(ge, ge.getHttpStatusCode());
+        }
+      }
+    } catch (IOException ioe) {
+      throw new RewritingException(ioe, HttpResponse.SC_INTERNAL_SERVER_ERROR);
+    }
+  }
+
+  /**
+   * Rewrite the CSS content in a style DOM node.
+   * @param styleNode Rewrite the CSS content of this node
+   * @param source Uri of content
+   * @param rewriter Rewrite urls
+   * @param extractImports If true remove the import statements from the output and return their
+   *            referenced URIs.
+   * @return Empty list of extracted import URIs.
+   */
+  public List<String> rewrite(Element styleNode, Uri source,
+      UriMaker uriMaker, boolean extractImports) throws RewritingException {
+    try {
+      List<Object> stylesheet = cssParser.parse(styleNode.getTextContent());
+      List<String> imports = rewrite(stylesheet, source, uriMaker, extractImports);
+      // Write the rewritten CSS back into the element
+      String content = cssParser.serialize(stylesheet);
+      if (StringUtils.isEmpty(content) || StringUtils.isWhitespace(content)) {
+        // Remove the owning node
+        styleNode.getParentNode().removeChild(styleNode);
+      } else {
+        styleNode.setTextContent(content);
+      }
+      return imports;
+    } catch (GadgetException ge) {
+      if (ge.getCause() instanceof ParseException) {
+        logger.log(Level.WARNING,
+              "Caja CSS parse failure: " + ge.getCause().getMessage() + " for " + source);
+        return Collections.emptyList();
+      } else {
+        throw new RewritingException(ge, ge.getHttpStatusCode());
+      }
+    }
+  }
+
+  /**
+   * Rewrite the CSS DOM in place.
+   * @param styleSheet To rewrite
+   * @param source  Uri of content
+   * @param rewriter  Rewrite urls
+   * @param extractImports If true remove the import statements from the output and return their
+   *            referenced URIs.
+   * @return Empty list of extracted import URIs.
+   */
+  public static List<String> rewrite(List<Object> styleSheet, final Uri source,
+      final UriMaker uriMaker, final boolean extractImports) {
+    final List<String> imports = Lists.newLinkedList();
+
+    for (int i = styleSheet.size() - 1; i >= 0; i--) {
+      if (styleSheet.get(i) instanceof CajaCssLexerParser.ImportDecl) {
+        if (extractImports) {
+          imports.add(0, ((CajaCssLexerParser.ImportDecl)styleSheet.get(i)).getUri());
+          styleSheet.remove(i);
+        } else {
+          CajaCssLexerParser.ImportDecl importDecl = (CajaCssLexerParser.ImportDecl) styleSheet
+              .get(i);
+          importDecl.setUri(rewriteUri(uriMaker, importDecl.getUri(), source));
+        }
+      } else if (styleSheet.get(i) instanceof CajaCssLexerParser.UriDecl) {
+        CajaCssLexerParser.UriDecl uriDecl = (CajaCssLexerParser.UriDecl) styleSheet
+              .get(i);
+          uriDecl.setUri(rewriteUri(uriMaker, uriDecl.getUri(), source));
+      }
+    }
+    return imports;
+  }
+  
+  private static String rewriteUri(UriMaker uriMaker, String input, Uri context) {
+    Uri inboundUri = null;
+    try {
+      inboundUri = Uri.parse(input);
+    } catch (IllegalArgumentException e) {
+      // Don't rewrite at all.
+      return input;
+    }
+    if (context != null) {
+      inboundUri = context.resolve(inboundUri);
+    }
+    ProxyUriManager.ProxyUri proxyUri =
+        new ProxyUriManager.ProxyUri(DomWalker.makeGadget(context), inboundUri);
+    return uriMaker.make(proxyUri, context).toString();
+  }
+  
+  public static UriMaker uriMaker(ProxyUriManager wrapped, ContentRewriterFeature.Config config) {
+    return new UriMaker(wrapped, config);
+  }
+  
+  public static class UriMaker {
+    private final ProxyUriManager wrapped;
+    private final ContentRewriterFeature.Config config;
+    
+    private UriMaker(ProxyUriManager wrapped, ContentRewriterFeature.Config config) {
+      this.wrapped = wrapped;
+      this.config = config;
+    }
+    
+    public Uri make(ProxyUriManager.ProxyUri uri, Uri context) {
+      if (config.shouldRewriteURL(uri.getResource().toString())) {
+        List<ProxyUriManager.ProxyUri> puris = Lists.newArrayList(uri);
+        List<Uri> returned = wrapped.make(puris, null);
+        return returned.get(0);
+      }
+      return context.resolve(uri.getResource());
+    }
+  }
+}
+

Added: shindig/trunk/java/gadgets/src/test/java/org/apache/shindig/gadgets/rewrite/CssRequestRewriterTest.java
URL: http://svn.apache.org/viewvc/shindig/trunk/java/gadgets/src/test/java/org/apache/shindig/gadgets/rewrite/CssRequestRewriterTest.java?rev=920559&view=auto
==============================================================================
--- shindig/trunk/java/gadgets/src/test/java/org/apache/shindig/gadgets/rewrite/CssRequestRewriterTest.java (added)
+++ shindig/trunk/java/gadgets/src/test/java/org/apache/shindig/gadgets/rewrite/CssRequestRewriterTest.java Mon Mar  8 22:59:42 2010
@@ -0,0 +1,226 @@
+/*
+ * 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.rewrite;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+
+import org.apache.commons.io.IOUtils;
+import org.apache.commons.lang.StringUtils;
+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.parse.caja.CajaCssLexerParser;
+import org.apache.shindig.gadgets.uri.PassthruManager;
+import org.apache.shindig.gadgets.uri.ProxyUriManager;
+import org.easymock.EasyMock;
+import org.junit.Before;
+import org.junit.Test;
+
+import java.io.StringReader;
+import java.io.StringWriter;
+import java.util.List;
+
+import com.google.common.collect.Lists;
+
+/**
+ *
+ */
+public class CssRequestRewriterTest extends RewriterTestBase {
+  private CssRequestRewriter rewriter;
+  private CssRequestRewriter rewriterNoOverrideExpires;
+  private Uri dummyUri;
+  private ProxyUriManager proxyUriManager;
+  private ContentRewriterFeature.Factory factory;
+
+  @Override
+  @Before
+  public void setUp() throws Exception {
+    super.setUp();
+    final ContentRewriterFeature.Config overrideFeatureNoOverrideExpires =
+        rewriterFeatureFactory.get(createSpecWithRewrite(".*", ".*exclude.*", null, tags));
+    ContentRewriterFeature.Factory factoryNoOverrideExpires =
+        new ContentRewriterFeature.Factory(null, null) {
+          @Override
+          public ContentRewriterFeature.Config get(HttpRequest req) {
+            return overrideFeatureNoOverrideExpires;
+          }
+        };
+    proxyUriManager = new PassthruManager("www.test.com", "/dir/proxy");
+    rewriterNoOverrideExpires = new CssRequestRewriter(new CajaCssLexerParser(),
+        proxyUriManager, factoryNoOverrideExpires);
+    final ContentRewriterFeature.Config overrideFeature =
+        rewriterFeatureFactory.get(createSpecWithRewrite(".*", ".*exclude.*", "3600", tags));
+    factory = new ContentRewriterFeature.Factory(null, null) {
+      @Override
+      public ContentRewriterFeature.Config get(HttpRequest req) {
+        return overrideFeature;
+      }
+    };
+    
+    rewriter = new CssRequestRewriter(new CajaCssLexerParser(),
+        proxyUriManager, factory);
+    dummyUri = Uri.parse("http://www.w3c.org");
+  }
+
+  @Test
+  public void testCssBasic() throws Exception {
+    String content = IOUtils.toString(this.getClass().getClassLoader().
+        getResourceAsStream("org/apache/shindig/gadgets/rewrite/rewritebasic.css"));
+    String expected = IOUtils.toString(this.getClass().getClassLoader().
+        getResourceAsStream("org/apache/shindig/gadgets/rewrite/rewritebasic-expected.css"));
+    HttpRequest request = new HttpRequest(Uri.parse("http://www.example.org/path/rewritebasic.css"));
+    request.setMethod("GET");
+    request.setGadget(SPEC_URL);
+
+    HttpResponse response = new HttpResponseBuilder().setHeader("Content-Type", "text/css")
+      .setResponseString(content).create();
+
+    MutableContent mc = new MutableContent(null, content);
+    rewriter.rewrite(request, response, mc);
+
+    assertEquals(StringUtils.deleteWhitespace(expected),
+        StringUtils.deleteWhitespace(mc.getContent()));
+  }
+
+  @Test
+  public void testCssBasicNoOverrideExpires() throws Exception {
+    String content = IOUtils.toString(this.getClass().getClassLoader().
+        getResourceAsStream("org/apache/shindig/gadgets/rewrite/rewritebasic.css"));
+    String expected = IOUtils.toString(this.getClass().getClassLoader().
+        getResourceAsStream("org/apache/shindig/gadgets/rewrite/rewritebasic-expected.css"));
+    expected = expected.replaceAll("refresh=3600", "refresh=86400");
+    HttpRequest request = new HttpRequest(Uri.parse("http://www.example.org/path/rewritebasic.css"));
+    request.setMethod("GET");
+    request.setGadget(SPEC_URL);
+
+    HttpResponse response = new HttpResponseBuilder().setHeader("Content-Type", "text/css")
+      .setResponseString(content).create();
+
+    MutableContent mc = new MutableContent(null, content);
+    rewriterNoOverrideExpires.rewrite(request, response, mc);
+
+    assertEquals(StringUtils.deleteWhitespace(expected),
+        StringUtils.deleteWhitespace(mc.getContent()));
+  }
+
+  @Test
+  public void testCssBasicNoCache() throws Exception {
+    String content = IOUtils.toString(this.getClass().getClassLoader().
+        getResourceAsStream("org/apache/shindig/gadgets/rewrite/rewritebasic.css"));
+    String expected = IOUtils.toString(this.getClass().getClassLoader().
+        getResourceAsStream("org/apache/shindig/gadgets/rewrite/rewritebasic-expected.css"));
+    expected = expected.replaceAll("fp=1150739864", "fp=1150739864&nocache=1");
+    HttpRequest request = new HttpRequest(Uri.parse("http://www.example.org/path/rewritebasic.css"));
+    request.setMethod("GET");
+    request.setGadget(SPEC_URL);
+    request.setIgnoreCache(true);
+
+    HttpResponse response = new HttpResponseBuilder().setHeader("Content-Type", "text/css")
+      .setResponseString(content).create();
+
+    MutableContent mc = new MutableContent(null, content);
+    rewriter.rewrite(request, response, mc);
+
+    assertEquals(StringUtils.deleteWhitespace(expected),
+        StringUtils.deleteWhitespace(mc.getContent()));
+  }
+
+  @Test
+  public void testCssWithContainerProxy() throws Exception {
+    String content = IOUtils.toString(this.getClass().getClassLoader().
+        getResourceAsStream("org/apache/shindig/gadgets/rewrite/rewritebasic.css"));
+    String expected = IOUtils.toString(this.getClass().getClassLoader().
+        getResourceAsStream("org/apache/shindig/gadgets/rewrite/rewritebasic-expected.css"));
+    expected = replaceDefaultWithMockServer(expected);
+    proxyUriManager = new PassthruManager("www.mock.com", "/dir/proxy");
+    rewriter = new CssRequestRewriter(new CajaCssLexerParser(),
+        proxyUriManager, factory);
+    
+    HttpRequest request = new HttpRequest(Uri.parse("http://www.example.org/path/rewritebasic.css"));
+    request.setMethod("GET");
+    request.setGadget(SPEC_URL);
+    request.setContainer(MOCK_CONTAINER);
+
+    HttpResponse response = new HttpResponseBuilder().setHeader("Content-Type", "text/css")
+      .setResponseString(content).create();
+
+    MutableContent mc = new MutableContent(null, content);
+    rewriter.rewrite(request, response, mc);
+
+    assertEquals(StringUtils.deleteWhitespace(expected),
+        StringUtils.deleteWhitespace(mc.getContent()));
+  }
+
+  @Test
+  public void testNoRewriteUnknownMimeType() throws Exception {
+    MutableContent mc = control.createMock(MutableContent.class); 
+    HttpRequest req = control.createMock(HttpRequest.class);
+    EasyMock.expect(req.getRewriteMimeType()).andReturn("unknown");
+    control.replay();
+    assertFalse(rewriter.rewrite(req, fakeResponse, mc));
+    control.verify();
+  }
+
+  private void validateRewritten(String content, Uri base, String expected) throws Exception {
+    MutableContent mc = new MutableContent(null, content);
+    HttpRequest request = new HttpRequest(base);
+    rewriter.rewrite(request,
+        new HttpResponseBuilder().setHeader("Content-Type", "text/css").create(), mc);
+    assertEquals(StringUtils.deleteWhitespace(expected),
+        StringUtils.deleteWhitespace(mc.getContent()));
+  }
+
+  private void validateRewritten(String content, String expected) throws Exception {
+    validateRewritten(content, dummyUri, expected);
+  }
+
+  @Test
+  public void testUrlDeclarationRewrite() throws Exception {
+    String original =
+        "div {list-style-image:url('http://a.b.com/bullet.gif');list-style-position:outside;margin:5px;padding:0}\n" +
+         ".someid {background-image:url(http://a.b.com/bigimg.png);float:right;width:165px;height:23px;margin-top:4px;margin-left:5px}";
+    String rewritten =
+        "div {list-style-image:url('http://www.test.com/dir/proxy?url=http%3A%2F%2Fa.b.com%2Fbullet.gif');\n"
+            + "list-style-position:outside;margin:5px;padding:0}\n"
+            + ".someid {background-image:url('http://www.test.com/dir/proxy?url=http%3A%2F%2Fa.b.com%2Fbigimg.png');\n"
+            + "float:right;width:165px;height:23px;margin-top:4px;margin-left:5px}";
+    validateRewritten(original, rewritten);
+  }
+
+  @Test
+  public void testExtractImports() throws Exception {
+    String original = " @import url(www.example.org/some.css);\n" +
+        "@import url('www.example.org/someother.css');\n" +
+        "@import url(\"www.example.org/another.css\");\n" +
+        " div { color: blue; }\n" +
+        " p { color: black; }\n" +
+        " span { color: red; }";
+    String expected = " div { color: blue; }\n" +
+        " p { color: black; }\n" +
+        " span { color: red; }";
+    StringWriter sw = new StringWriter();
+    List<String> stringList = rewriter
+        .rewrite(new StringReader(original), dummyUri,
+          CssRequestRewriter.uriMaker(proxyUriManager, defaultRewriterFeature), sw, true);
+    assertEquals(expected, sw.toString());
+    assertEquals(stringList, Lists.newArrayList("www.example.org/some.css",
+        "www.example.org/someother.css", "www.example.org/another.css"));
+  }
+}