Author: lryan
Date: Tue May  6 13:01:12 2008
New Revision: 653895

URL: http://svn.apache.org/viewvc?rev=653895&view=rev
Log:
Allow for cache header control using 'refresh' param on non-json proxy requests
Fix locked domain check to do allow for subdomains of embed-host.
Support for signed-preloads. 
Various fixes to cache management

Added:
    
incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/AbstractContentCache.java
Modified:
    incubator/shindig/trunk/features/core.io/io.js
    
incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/BasicContentCache.java
    
incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/GadgetServer.java
    
incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/HashLockedDomainService.java
    
incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/LockedDomainService.java
    
incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/RemoteContentRequest.java
    
incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/SigningFetcher.java
    
incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/http/HttpUtil.java
    
incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/http/ProxyHandler.java
    
incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/spec/Preload.java
    
incubator/shindig/trunk/java/gadgets/src/test/java/org/apache/shindig/gadgets/GadgetServerTest.java
    
incubator/shindig/trunk/java/gadgets/src/test/java/org/apache/shindig/gadgets/SigningFetcherTest.java

Modified: incubator/shindig/trunk/features/core.io/io.js
URL: 
http://svn.apache.org/viewvc/incubator/shindig/trunk/features/core.io/io.js?rev=653895&r1=653894&r2=653895&view=diff
==============================================================================
--- incubator/shindig/trunk/features/core.io/io.js (original)
+++ incubator/shindig/trunk/features/core.io/io.js Tue May  6 13:01:12 2008
@@ -217,7 +217,7 @@
   function respondWithPreload(postData, params, callback) {
     if (gadgets.io.preloaded_ && gadgets.io.preloaded_[postData.url]) {
       var preload = gadgets.io.preloaded_[postData.url];
-      if (postData.httpMethod == "GET" && postData.authz == "none") {
+      if (postData.httpMethod == "GET") {
         delete gadgets.io.preloaded_[postData.url];
         if (preload.rc !== 200) {
           callback({errors : ["Error " + preload.rc]});
@@ -288,6 +288,8 @@
           params.REFRESH_INTERVAL = 3600;
          }
       }
+      var signOwner = params.OWNER_SIGNED;
+      var signViewer = params.VIEWER_SIGNED;
 
       var headers = params.HEADERS || {};
       if (params.METHOD === "POST" && !headers["Content-Type"]) {
@@ -303,7 +305,9 @@
         st : st || "",
         oauthState : reqState || "",
         oauthService : oauthService || "",
-        oauthToken : oauthToken || ""
+        oauthToken : oauthToken || "",
+        signOwner : signOwner || "true",
+        signViewer : signViewer || "true"
       };
 
       if (!respondWithPreload(paramData, params, callback, processResponse)) {

Added: 
incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/AbstractContentCache.java
URL: 
http://svn.apache.org/viewvc/incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/AbstractContentCache.java?rev=653895&view=auto
==============================================================================
--- 
incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/AbstractContentCache.java
 (added)
+++ 
incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/AbstractContentCache.java
 Tue May  6 13:01:12 2008
@@ -0,0 +1,182 @@
+/*
+ * 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;
+
+import java.net.URI;
+import java.text.DateFormat;
+import java.text.ParseException;
+import java.text.SimpleDateFormat;
+import java.util.Arrays;
+import java.util.Date;
+import java.util.List;
+
+/**
+ * Base class for content caches. Defines cache expiration rules and
+ * and restrictions on allowed content.
+ *
+ * TODO: Move cache checking code into HttpUtil
+ */
+public abstract class AbstractContentCache implements ContentCache {
+
+  /**
+   * Used to parse Expires: header.
+   */
+  private final static DateFormat dateFormat
+      = new SimpleDateFormat("EEE, d MMM yyyy HH:mm:ss Z");
+
+  public final RemoteContent getContent(RemoteContentRequest request) {
+    if (canCacheRequest(request)) {
+      return getContent(request.getUri());
+    }
+    return null;
+  }
+
+  public final RemoteContent getContent(URI uri) {
+    if (uri == null) return null;
+    return checkContent(getContentImpl(uri));
+  }
+
+  protected abstract RemoteContent getContentImpl(URI uri);
+
+  public void addContent(RemoteContentRequest request, RemoteContent content) {
+    if (canCacheRequest(request)) {
+      addContent(request.getUri(), content);
+    }
+  }
+
+  public void addContent(URI uri, RemoteContent content) {
+    content = checkContent(content);
+    if (uri == null || content == null) return;
+    // Clone the URI to prevent outside references from preventing collection
+    addContentImpl(URI.create(uri.toString()), content);
+  }
+
+  protected abstract void addContentImpl(URI uri, RemoteContent content);
+
+  public RemoteContent removeContent(RemoteContentRequest request) {
+    return removeContent(request.getUri());
+  }
+
+  public RemoteContent removeContent(URI uri) {
+    if (uri == null) return null;
+    RemoteContent content = getContentImpl(uri);
+    removeContentImpl(uri);
+    return checkContent(content);
+  }
+
+  protected abstract RemoteContent removeContentImpl(URI uri);
+
+  /**
+   * Utility function to verify that an entry is cacheable and not expired
+   * Returns null if the content is no longer cacheable.
+   *
+   * @param request
+   * @return content or null
+   */
+  protected boolean canCacheRequest(RemoteContentRequest request) {
+    return ("GET".equals(request.getMethod()) &&
+        !request.getOptions().ignoreCache);
+  }
+
+  /**
+   * Utility function to verify that an entry is cacheable and not expired
+   * Returns null if the content is no longer cacheable.
+   *
+   * @param content
+   * @return content or null
+   */
+  protected RemoteContent checkContent(RemoteContent content) {
+    if (content == null) return null;
+
+    if (content.getHttpStatusCode() != 200) return null;
+
+    long now = System.currentTimeMillis();
+
+    String expires = content.getHeader("Expires");
+    if (expires != null) {
+      try {
+        Date expiresDate = dateFormat.parse(expires);
+        long expiresMs = expiresDate.getTime();
+        if (expiresMs > now) {
+          return content;
+        } else {
+          return null;
+        }
+      } catch (ParseException e) {
+        return null;
+      }
+    }
+
+    // Cache-Control headers may be an explicit max-age, or no-cache, which
+    // means we use a default expiration time.
+    String cacheControl = content.getHeader("Cache-Control");
+    if (cacheControl != null) {
+      String[] directives = cacheControl.split(",");
+      for (String directive : directives) {
+        directive = directive.trim();
+        // boolean params
+        if (directive.equals("no-cache")) {
+          return null;
+        }
+        if (directive.startsWith("max-age")) {
+          String[] parts = directive.split("=");
+          if (parts.length == 2) {
+            try {
+              // Record the max-age and store it in the content as an
+              // absolute expiration
+              long maxAgeMs = Long.parseLong(parts[1]) * 1000;
+              Date newExpiry = new Date(now + maxAgeMs);
+              content.getAllHeaders()
+                  .put("Expires", Arrays.asList(dateFormat.format(newExpiry)));
+              return content;
+            } catch (NumberFormatException e) {
+              return null;
+            }
+          }
+        }
+      }
+    }
+
+    // Look for Pragma: no-cache. If present, return null.
+    List<String> pragmas = content.getHeaders("Pragma");
+    if (pragmas != null) {
+      for (String pragma : pragmas) {
+        if ("no-cache".equals(pragma)) {
+          return null;
+        }
+      }
+    }
+
+    // Assume the content is cacheable for the default TTL
+    // if no other directives exist
+    Date newExpiry = new Date(now + getDefaultTTL());
+    content.getAllHeaders()
+        .put("Expires", Arrays.asList(dateFormat.format(newExpiry)));
+    return content;
+  }
+
+  /**
+   * Default TTL for an entry in the cache that does not have any
+   * cache controlling headers
+   * @return default TTL for cache entries
+   */
+  protected long getDefaultTTL() {
+    // 5 mins
+    return 5L * 60L * 1000L;
+  }
+}

Modified: 
incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/BasicContentCache.java
URL: 
http://svn.apache.org/viewvc/incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/BasicContentCache.java?rev=653895&r1=653894&r2=653895&view=diff
==============================================================================
--- 
incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/BasicContentCache.java
 (original)
+++ 
incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/BasicContentCache.java
 Tue May  6 13:01:12 2008
@@ -18,161 +18,26 @@
 package org.apache.shindig.gadgets;
 
 import java.net.URI;
-import java.text.DateFormat;
-import java.text.ParseException;
-import java.text.SimpleDateFormat;
-import java.util.Arrays;
-import java.util.Date;
-import java.util.List;
 import java.util.Map;
 import java.util.WeakHashMap;
 
 /**
- * Simple cache of RemoteContent 
+ * Simple cache of RemoteContent. Uses WeakHashMap for memory management
  */
-public class BasicContentCache implements ContentCache {
-
-  /**
-   * Used to parse Expires: header.
-   */
-  private final static DateFormat dateFormat
-      = new SimpleDateFormat("EEE, d MMM yyyy HH:mm:ss Z");
+public class BasicContentCache extends AbstractContentCache {
 
   private final Map<URI, RemoteContent> cache
       = new WeakHashMap<URI, RemoteContent>();
 
-  public RemoteContent getContent(URI uri) {
-    if (uri == null) return null;
-    return checkContent(cache.get(uri));
-  }
-
-  public RemoteContent getContent(RemoteContentRequest request) {
-    if (canCacheRequest(request)) {
-      return getContent(request.getUri());
-    }
-    return null;
-  }
-
-  public void addContent(URI uri, RemoteContent content) {
-    content = checkContent(content);
-    if (uri == null || content == null) return;
-    // Clone the URI to prevent outside references from preventing collection
-    cache.put(URI.create(uri.toString()), content);
-  }
-
-  public void addContent(RemoteContentRequest request, RemoteContent content) {
-    if (canCacheRequest(request)) {
-      addContent(request.getUri(), content);
-    }
-  }
-
-  public RemoteContent removeContent(URI uri) {
-    if (uri == null) return null;
-    RemoteContent content = cache.get(uri);
-    cache.remove(uri);
-    return checkContent(content);
-  }
-
-  public RemoteContent removeContent(RemoteContentRequest request) {
-    return removeContent(request.getUri());
-  }
-
-  /**
-   * Utility function to verify that an entry is cacheable and not expired
-   * Returns null if the content is no longer cacheable.
-   *
-   * @param request
-   * @return content or null
-   */
-  protected boolean canCacheRequest(RemoteContentRequest request) {
-    return ("GET".equals(request.getMethod()) &&
-        !request.getOptions().ignoreCache);
+  protected RemoteContent getContentImpl(URI uri) {
+    return cache.get(uri);
   }
 
-  /**
-   * Utility function to verify that an entry is cacheable and not expired
-   * Returns null if the content is no longer cacheable.
-   *
-   * @param content
-   * @return content or null
-   */
-  protected RemoteContent checkContent(RemoteContent content) {
-    if (content == null) return null;
-
-    if (content.getHttpStatusCode() != 200) return null;
-
-    long now = System.currentTimeMillis();
-
-    String expires = content.getHeader("Expires");
-    if (expires != null) {
-      try {
-        Date expiresDate = dateFormat.parse(expires);
-        long expiresMs = expiresDate.getTime();
-        if (expiresMs > now) {
-          return content;
-        } else {
-          return null;
-        }
-      } catch (ParseException e) {
-        return null;
-      }
-    }
-
-    // Cache-Control headers may be an explicit max-age, or no-cache, which
-    // means we use a default expiration time.
-    String cacheControl = content.getHeader("Cache-Control");
-    if (cacheControl != null) {
-      String[] directives = cacheControl.split(",");
-      for (String directive : directives) {
-        directive = directive.trim();
-        // boolean params
-        if (directive.equals("no-cache")) {
-          return null;
-        }
-        if (directive.startsWith("max-age")) {
-          String[] parts = directive.split("=");
-          if (parts.length == 2) {
-            try {
-              // Record the max-age and store it in the content as an
-              // absolute expiration
-              long maxAgeMs = Long.parseLong(parts[1]) * 1000;
-              Date newExpiry = new Date(now + maxAgeMs);
-              content.getAllHeaders()
-                  .put("Expires", Arrays.asList(dateFormat.format(newExpiry)));
-              return content;
-            } catch (NumberFormatException e) {
-              return null;
-            }
-          }
-        }
-      }
-    }
-
-    // Look for Pragma: no-cache. If present, return null.
-    List<String> pragmas = content.getHeaders("Pragma");
-    if (pragmas != null) {
-      for (String pragma : pragmas) {
-        if ("no-cache".equals(pragma)) {
-          return null;
-        }
-      }
-    }
-
-    // Assume the content is cacheable for the default TTL
-    // if no other directives exist
-    Date newExpiry = new Date(now + getDefaultTTL());
-    content.getAllHeaders()
-        .put("Expires", Arrays.asList(dateFormat.format(newExpiry)));
-    return content;
+  protected void addContentImpl(URI uri, RemoteContent content) {
+    cache.put(uri, content);
   }
 
-  /**
-   * Default TTL for an entry in the cache that does not have any
-   * cache controlling headers
-   * @return default TTL for cache entries
-   */
-  protected long getDefaultTTL() {
-    // 5 mins
-    return 5L * 60L * 1000L;
+  protected RemoteContent removeContentImpl(URI uri) {
+    return cache.remove(uri);
   }
 }

Modified: 
incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/GadgetServer.java
URL: 
http://svn.apache.org/viewvc/incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/GadgetServer.java?rev=653895&r1=653894&r2=653895&view=diff
==============================================================================
--- 
incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/GadgetServer.java
 (original)
+++ 
incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/GadgetServer.java
 Tue May  6 13:01:12 2008
@@ -17,6 +17,7 @@
  */
 package org.apache.shindig.gadgets;
 
+import org.apache.shindig.gadgets.spec.Auth;
 import org.apache.shindig.gadgets.spec.Feature;
 import org.apache.shindig.gadgets.spec.GadgetSpec;
 import org.apache.shindig.gadgets.spec.LocaleSpec;
@@ -201,10 +202,16 @@
       CompletionService<RemoteContent> preloadProcessor
           = new ExecutorCompletionService<RemoteContent>(executor);
       for (Preload preload : gadget.getSpec().getModulePrefs().getPreloads()) {
-        PreloadTask task = new PreloadTask(gadget.getContext(), preload,
-            preloadFetcherFactory);
-        Future<RemoteContent> future = preloadProcessor.submit(task);
-        gadget.getPreloadMap().put(preload, future);
+        // Cant execute signed/oauth preloads without the token
+        if ((preload.getAuth() == Auth.NONE ||
+            gadget.getContext().getToken() != null) &&
+            (preload.getViews().size() == 0 ||
+            preload.getViews().contains(gadget.getContext().getView()))) {
+          PreloadTask task = new PreloadTask(gadget.getContext(), preload,
+              preloadFetcherFactory);
+          Future<RemoteContent> future = preloadProcessor.submit(task);
+          gadget.getPreloadMap().put(preload, future);
+        }
       }
     }
 
@@ -341,6 +348,8 @@
 
   public RemoteContent call() {
     RemoteContentRequest request = new RemoteContentRequest(preload.getHref());
+    request.getOptions().ownerSigned = preload.isSignOwner();
+    request.getOptions().viewerSigned = preload.isSignViewer();
     try {
       switch (preload.getAuth()) {
         case NONE:

Modified: 
incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/HashLockedDomainService.java
URL: 
http://svn.apache.org/viewvc/incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/HashLockedDomainService.java?rev=653895&r1=653894&r2=653895&view=diff
==============================================================================
--- 
incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/HashLockedDomainService.java
 (original)
+++ 
incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/HashLockedDomainService.java
 Tue May  6 13:01:12 2008
@@ -17,10 +17,6 @@
  */
 package org.apache.shindig.gadgets;
 
-import java.util.HashSet;
-import java.util.Map;
-import java.util.Set;
-
 import org.apache.commons.codec.digest.DigestUtils;
 import org.apache.shindig.gadgets.spec.Feature;
 import org.apache.shindig.util.Base32;
@@ -28,6 +24,10 @@
 import com.google.inject.Inject;
 import com.google.inject.name.Named;
 
+import java.util.HashSet;
+import java.util.Map;
+import java.util.Set;
+
 /**
  * Locked domain implementation based on sha1.
  * 
@@ -79,12 +79,16 @@
     }
   }
 
+  public boolean isEnabled() {
+    return enabled;
+  }
+
   public String getEmbedHost() {
     return embedHost;
   }
 
   public boolean embedCanRender(String host) {
-    return (!enabled || host.equals(embedHost));
+    return (!enabled || host.endsWith(embedHost));
   }
 
   public boolean gadgetCanRender(String host, Gadget gadget, String container) 
{

Modified: 
incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/LockedDomainService.java
URL: 
http://svn.apache.org/viewvc/incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/LockedDomainService.java?rev=653895&r1=653894&r2=653895&view=diff
==============================================================================
--- 
incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/LockedDomainService.java
 (original)
+++ 
incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/LockedDomainService.java
 Tue May  6 13:01:12 2008
@@ -28,6 +28,12 @@
 public interface LockedDomainService {
 
   /**
+   * Is locked domain enabled
+   * @return true is locked domain is enabled
+   */
+  public boolean isEnabled();
+
+  /**
    * Check whether embedded content (img src, for example) can render on
    * a particular host.
    * 

Modified: 
incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/RemoteContentRequest.java
URL: 
http://svn.apache.org/viewvc/incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/RemoteContentRequest.java?rev=653895&r1=653894&r2=653895&view=diff
==============================================================================
--- 
incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/RemoteContentRequest.java
 (original)
+++ 
incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/RemoteContentRequest.java
 Tue May  6 13:01:12 2008
@@ -205,13 +205,15 @@
    * Creates a new request to a different URL using all request data from
    * an existing request.
    *
+   * TODO - Need to copy by value
+   *
    * @param uri
    * @param base The base request to copy data from.
    */
   public RemoteContentRequest(URI uri, RemoteContentRequest base) {
     this.uri = uri;
     this.method = base.method;
-    this.options = base.options;
+    this.options = new Options(base.options);
     this.headers = base.headers;
     this.contentType = base.contentType;
     this.postBody = base.postBody;
@@ -351,5 +353,18 @@
    */
   public static class Options {
     public boolean ignoreCache = false;
+    public boolean ownerSigned = true;
+    public boolean viewerSigned = true;
+
+    public Options() {};
+
+    /**
+     * Copy constructor
+     */
+    public Options(Options copyFrom) {
+      this.ignoreCache = copyFrom.ignoreCache;
+      this.ownerSigned = copyFrom.ownerSigned;
+      this.viewerSigned = copyFrom.viewerSigned;
+    }
   }
 }

Modified: 
incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/SigningFetcher.java
URL: 
http://svn.apache.org/viewvc/incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/SigningFetcher.java?rev=653895&r1=653894&r2=653895&view=diff
==============================================================================
--- 
incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/SigningFetcher.java
 (original)
+++ 
incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/SigningFetcher.java
 Tue May  6 13:01:12 2008
@@ -174,7 +174,7 @@
     URI resource = request.getUri();
     String query = resource.getRawQuery();
     List<Parameter> cacheableParams = sanitize(OAuth.decodeForm(query));
-    addOpenSocialParams(cacheableParams);
+    addOpenSocialParams(request.getOptions(), cacheableParams);
     addOAuthNonTemporalParams(cacheableParams);
     String cacheableQuery = OAuth.formEncode(cacheableParams);
     URL url = new URL(
@@ -202,7 +202,7 @@
       msgParams.addAll(queryParams);
       msgParams.addAll(postParams);
 
-      addOpenSocialParams(msgParams);
+      addOpenSocialParams(req.getOptions(), msgParams);
 
       addOAuthParams(msgParams);
 
@@ -259,14 +259,15 @@
   }
 
 
-  private void addOpenSocialParams(List<Parameter> msgParams) {
+  private void addOpenSocialParams(RemoteContentRequest.Options options,
+      List<Parameter> msgParams) {
     String owner = authToken.getOwnerId();
-    if (owner != null) {
+    if (owner != null && options.ownerSigned) {
       msgParams.add(new OAuth.Parameter(OPENSOCIAL_OWNERID, owner));
     }
 
     String viewer = authToken.getViewerId();
-    if (viewer != null) {
+    if (viewer != null && options.viewerSigned) {
       msgParams.add(new OAuth.Parameter(OPENSOCIAL_VIEWERID, viewer));
     }
 

Modified: 
incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/http/HttpUtil.java
URL: 
http://svn.apache.org/viewvc/incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/http/HttpUtil.java?rev=653895&r1=653894&r2=653895&view=diff
==============================================================================
--- 
incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/http/HttpUtil.java
 (original)
+++ 
incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/http/HttpUtil.java
 Tue May  6 13:01:12 2008
@@ -19,16 +19,23 @@
 
 package org.apache.shindig.gadgets.http;
 
+import org.apache.shindig.gadgets.ContainerConfig;
 import org.apache.shindig.gadgets.Gadget;
 import org.apache.shindig.gadgets.GadgetContext;
-import org.apache.shindig.gadgets.ContainerConfig;
 import org.apache.shindig.gadgets.spec.GadgetSpec;
 import org.apache.shindig.gadgets.spec.View;
-
 import org.json.JSONArray;
 import org.json.JSONException;
 import org.json.JSONObject;
 
+import java.text.DateFormat;
+import java.text.SimpleDateFormat;
+import java.util.Arrays;
+import java.util.Date;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Locale;
+import java.util.Map;
 import java.util.Set;
 
 import javax.servlet.http.HttpServletResponse;
@@ -41,6 +48,9 @@
   // 1 year.
   private static final int DEFAULT_TTL = 60 * 60 * 24 * 365;
 
+   public static final DateFormat DATE_HEADER_FORMAT = new SimpleDateFormat(
+      "EEE, dd MMM yyyy HH:mm:ss zzz", Locale.US);
+
   /**
    * Sets default caching Headers (Expires, Cache-Control, Last-Modified)
    *
@@ -73,7 +83,74 @@
     }
     // Firefox requires this for certain cases.
     response.setDateHeader("Last-Modified", START_TIME);
+  }
+
+  /**
+   * Takes a set of recevied HTTP headers and adjusts them to enforce
+   * a desired cache lifetime
+   */
+  public static Map<String, List<String>> enforceCachePolicy(
+      Map<String, List<String>> originalHeaders,
+      long age,
+      boolean ignoreOriginalCacheControl) {
+
+    Map<String, List<String>> newHeaders = new HashMap<String, List<String>>(
+        originalHeaders);
+
+    long originalAge = 0L;
+    if (newHeaders.containsKey("Expires")) {
+      try {
+        Date date = DATE_HEADER_FORMAT
+            .parse(originalHeaders.get("Expires").get(0));
+        originalAge = date.getTime() - System.currentTimeMillis();
+      } catch (Exception e) {
+        // Dont care
+      }
+    }
+
+    if (newHeaders.containsKey("Cache-Control")) {
+      try {
+        String cacheControl = originalHeaders.get("Cache-Control").get(0);
+        if (cacheControl != null) {
+          String[] directives = cacheControl.split(",");
+          for (String directive : directives) {
+            directive = directive.trim();
+            // boolean params
+            if (directive.equals("no-cache") || directive.equals("no-store")) {
+              // Always respect no-cache or no-store no matter what
+              return newHeaders;
+            }
+            if (directive.startsWith("max-age")) {
+              String[] parts = directive.split("=");
+              if (parts.length == 2) {
+                // Record the max-age and store it in the content as an
+                // absolute expiration
+                originalAge = Long.parseLong(parts[1]) * 1000;
+              }
+            }
+          }
+        }
+      } catch (Exception e) {
+        // Dont care
+      }
+    }
+
+    if (!ignoreOriginalCacheControl && age < originalAge) {
+      age = originalAge;
+    }
 
+    // Strip the original
+    newHeaders.remove("Expires");
+    newHeaders.remove("Cache-Control");
+
+    // Replace with new consistent headers
+    newHeaders.put("Expires", Arrays.asList(
+        DATE_HEADER_FORMAT.format(new Date(age + 
System.currentTimeMillis()))));
+
+    newHeaders.put("Cache-Control",
+        Arrays.asList("public, max-age=" + (age / 1000L)));
+    
+    return newHeaders;
   }
 
   /**

Modified: 
incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/http/ProxyHandler.java
URL: 
http://svn.apache.org/viewvc/incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/http/ProxyHandler.java?rev=653895&r1=653894&r2=653895&view=diff
==============================================================================
--- 
incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/http/ProxyHandler.java
 (original)
+++ 
incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/http/ProxyHandler.java
 Tue May  6 13:01:12 2008
@@ -18,6 +18,23 @@
  */
 package org.apache.shindig.gadgets.http;
 
+import org.apache.shindig.gadgets.ContentFetcher;
+import org.apache.shindig.gadgets.ContentFetcherFactory;
+import org.apache.shindig.gadgets.GadgetException;
+import org.apache.shindig.gadgets.GadgetToken;
+import org.apache.shindig.gadgets.GadgetTokenDecoder;
+import org.apache.shindig.gadgets.LockedDomainService;
+import org.apache.shindig.gadgets.RemoteContent;
+import org.apache.shindig.gadgets.RemoteContentRequest;
+import org.apache.shindig.gadgets.oauth.OAuthRequestParams;
+import org.apache.shindig.gadgets.spec.Auth;
+import org.apache.shindig.gadgets.spec.Preload;
+import org.apache.shindig.util.InputStreamConsumer;
+import org.json.JSONException;
+import org.json.JSONObject;
+
+import com.google.inject.Inject;
+
 import java.io.IOException;
 import java.io.UnsupportedEncodingException;
 import java.net.URI;
@@ -36,23 +53,6 @@
 import javax.servlet.http.HttpServletRequest;
 import javax.servlet.http.HttpServletResponse;
 
-import org.apache.shindig.gadgets.ContentFetcher;
-import org.apache.shindig.gadgets.ContentFetcherFactory;
-import org.apache.shindig.gadgets.GadgetException;
-import org.apache.shindig.gadgets.GadgetToken;
-import org.apache.shindig.gadgets.GadgetTokenDecoder;
-import org.apache.shindig.gadgets.LockedDomainService;
-import org.apache.shindig.gadgets.RemoteContent;
-import org.apache.shindig.gadgets.RemoteContentRequest;
-import org.apache.shindig.gadgets.oauth.OAuthRequestParams;
-import org.apache.shindig.gadgets.spec.Auth;
-import org.apache.shindig.gadgets.spec.Preload;
-import org.apache.shindig.util.InputStreamConsumer;
-import org.json.JSONException;
-import org.json.JSONObject;
-
-import com.google.inject.Inject;
-
 public class ProxyHandler {
   public static final String UNPARSEABLE_CRUFT = "throw 1; < don't be evil' >";
   public static final String POST_DATA_PARAM = "postData";
@@ -60,9 +60,11 @@
   public static final String SECURITY_TOKEN_PARAM = "st";
   public static final String HEADERS_PARAM = "headers";
   public static final String NOCACHE_PARAM = "nocache";
+  public static final String SIGN_VIEWER = "signViewer";
+  public static final String SIGN_OWNER = "signOwner";
   public static final String URL_PARAM = "url";
   private static final String REFRESH_PARAM = "refresh";
-  
+
   private static final Logger logger = 
       Logger.getLogger(ProxyHandler.class.getPackage().getName());
 
@@ -196,6 +198,15 @@
       RemoteContentRequest.Options options =
         new RemoteContentRequest.Options();
       options.ignoreCache = "1".equals(request.getParameter(NOCACHE_PARAM));
+      if (request.getParameter(SIGN_VIEWER) != null) {
+        options.viewerSigned = Boolean
+            .parseBoolean(request.getParameter(SIGN_VIEWER));
+      }
+      if (request.getParameter(SIGN_OWNER) != null) {
+        options.ownerSigned = Boolean
+            .parseBoolean(request.getParameter(SIGN_OWNER));
+      }
+
       return new RemoteContentRequest(
           method, url, headers, postBody, options);
     } catch (UnsupportedEncodingException e) {
@@ -286,14 +297,17 @@
     RemoteContentRequest rcr = buildRemoteContentRequest(request);
     RemoteContent results = contentFetcherFactory.get().fetch(rcr);
 
+    // Default interval of 1 hour
+    int refreshInterval = 60 * 60;
+    if (request.getParameter(REFRESH_PARAM) != null) {
+      refreshInterval =  Integer.valueOf(request.getParameter(REFRESH_PARAM));
+    }
+
     int status = results.getHttpStatusCode();
     response.setStatus(status);
     if (status == HttpServletResponse.SC_OK) {
-      Map<String, List<String>> headers = results.getAllHeaders();
-      if (headers.get("Cache-Control") == null) {
-        // Cache for 1 hour by default for static files.
-        HttpUtil.setCachingHeaders(response, 60 * 60);
-      }
+      Map<String, List<String>> headers = HttpUtil.enforceCachePolicy(
+          results.getAllHeaders(), refreshInterval * 1000L, false);
       for (Map.Entry<String, List<String>> entry : headers.entrySet()) {
         String name = entry.getKey();
         List<String> values = entry.getValue();

Modified: 
incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/spec/Preload.java
URL: 
http://svn.apache.org/viewvc/incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/spec/Preload.java?rev=653895&r1=653894&r2=653895&view=diff
==============================================================================
--- 
incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/spec/Preload.java
 (original)
+++ 
incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/spec/Preload.java
 Tue May  6 13:01:12 2008
@@ -22,6 +22,8 @@
 import org.w3c.dom.Element;
 
 import java.net.URI;
+import java.util.HashSet;
+import java.util.Set;
 
 /**
  * Represents an addressable piece of content that can be preloaded by the 
server
@@ -48,6 +50,30 @@
   }
 
   /**
+   * [EMAIL PROTECTED]
+   */
+  private final boolean signViewer;
+  public boolean isSignViewer() {
+    return signViewer;
+  }
+
+  /**
+   * [EMAIL PROTECTED]
+   */
+  private final boolean signOwner;
+  public boolean isSignOwner() {
+    return signOwner;
+  }
+
+  /**
+   * [EMAIL PROTECTED]
+   */
+  private final Set<String> views = new HashSet<String>();
+  public Set<String> getViews() {
+    return views;
+  }
+
+  /**
    * Produces an xml representation of the Preload.
    */
   @Override
@@ -64,11 +90,22 @@
    * @throws SpecParserException When the href is not specified
    */
   public Preload(Element preload) throws SpecParserException {
+    signOwner = XmlUtil.getBoolAttribute(preload, "sign_owner", true);
+    signViewer = XmlUtil.getBoolAttribute(preload, "sign_viewer", true);
     href = XmlUtil.getUriAttribute(preload, "href");
     if (href == null) {
       throw new SpecParserException("[EMAIL PROTECTED] is required.");
     }
 
+    // Record all the associated views
+    String viewNames = XmlUtil.getAttribute(preload, "views", "");
+    for (String s: viewNames.split(",")) {
+      s = s.trim();
+      if (s.length() > 0) {
+        views.add(s.trim());
+      }
+    }
+
     String authAttr = XmlUtil.getAttribute(preload, AUTHZ_ATTR);
     try {
       auth = Auth.parse(authAttr);
@@ -76,5 +113,4 @@
       throw new SpecParserException("Preload@" + ge.getMessage());
     }
   }
-
 }
\ No newline at end of file

Modified: 
incubator/shindig/trunk/java/gadgets/src/test/java/org/apache/shindig/gadgets/GadgetServerTest.java
URL: 
http://svn.apache.org/viewvc/incubator/shindig/trunk/java/gadgets/src/test/java/org/apache/shindig/gadgets/GadgetServerTest.java?rev=653895&r1=653894&r2=653895&view=diff
==============================================================================
--- 
incubator/shindig/trunk/java/gadgets/src/test/java/org/apache/shindig/gadgets/GadgetServerTest.java
 (original)
+++ 
incubator/shindig/trunk/java/gadgets/src/test/java/org/apache/shindig/gadgets/GadgetServerTest.java
 Tue May  6 13:01:12 2008
@@ -18,6 +18,7 @@
 package org.apache.shindig.gadgets;
 
 import org.apache.shindig.gadgets.spec.GadgetSpec;
+import org.apache.shindig.util.BlobCrypterException;
 import static org.easymock.EasyMock.eq;
 import static org.easymock.EasyMock.expect;
 
@@ -38,6 +39,21 @@
     public URI getUrl() {
       return SPEC_URL;
     }
+
+    @Override
+    @SuppressWarnings("unused")
+    public GadgetToken getToken() throws GadgetException {
+      try {
+        return new BasicGadgetToken("o", "v", "a", "d", "u", "m");
+      } catch (BlobCrypterException bce) {
+        throw new RuntimeException(bce);
+      }
+    }
+
+    @Override
+    public String getView() {
+      return "v2";
+    }
   };
   private final static String BASIC_SPEC_XML
       = "<Module>" +
@@ -130,6 +146,133 @@
                                     .get().getResponseAsString());
   }
 
+  public void testPreloadViewMatch() throws Exception {
+    String preloadUrl = "http://example.org/preload.txt";;
+    String preloadData = "Preload Data";
+    RemoteContentRequest preloadRequest
+        = new RemoteContentRequest(URI.create(preloadUrl));
+
+    String gadgetXml
+        = "<Module>" +
+          "  <ModulePrefs title=\"foo\">" +
+          "    <Preload href=\"" + preloadUrl + "\" views=\"v1\"/>" +
+          "  </ModulePrefs>" +
+          "  <Content type=\"html\" view=\"v1,v2\">dummy</Content>" +
+          "</Module>";
+    expect(fetcherFactory.get()).andReturn(fetcher);
+    expect(fetcher.fetch(SPEC_REQUEST))
+         .andReturn(new RemoteContent(gadgetXml));
+    expect(fetcher.fetch(preloadRequest))
+        .andReturn(new RemoteContent(preloadData));
+    replay();
+
+    GadgetContext context = new GadgetContext() {
+      @Override
+      public URI getUrl() {
+        return SPEC_URL;
+      }
+
+      @Override
+      public String getView() {
+        return "v1";
+      }
+    };
+
+    Gadget gadget = gadgetServer.processGadget(context);
+
+    assertTrue(gadget.getPreloadMap().size() == 1);
+  }
+
+  public void testPreloadAntiMatch() throws Exception {
+    String preloadUrl = "http://example.org/preload.txt";;
+    String preloadData = "Preload Data";
+    RemoteContentRequest preloadRequest
+        = new RemoteContentRequest(URI.create(preloadUrl));
+
+    String gadgetXml
+        = "<Module>" +
+        "  <ModulePrefs title=\"foo\">" +
+        "    <Preload href=\"" + preloadUrl + "\" views=\"v1,v3\"/>" +
+        "  </ModulePrefs>" +
+        "  <Content type=\"html\" view=\"v1,v2\">dummy</Content>" +
+        "</Module>";
+    expect(fetcherFactory.get()).andReturn(fetcher);
+    expect(fetcher.fetch(SPEC_REQUEST))
+        .andReturn(new RemoteContent(gadgetXml));
+    expect(fetcher.fetch(preloadRequest))
+        .andReturn(new RemoteContent(preloadData));
+    replay();
+
+    GadgetContext context = new GadgetContext() {
+      @Override
+      public URI getUrl() {
+        return SPEC_URL;
+      }
+
+      @Override
+      public String getView() {
+        return "v2";
+      }
+    };
+
+    Gadget gadget = gadgetServer.processGadget(context);
+    assertTrue(gadget.getPreloadMap().size() == 0);
+  }
+
+  public void testNoSignedPreloadWithoutToken() throws Exception {
+    String preloadUrl = "http://example.org/preload.txt";;
+    String preloadData = "Preload Data";
+    RemoteContentRequest preloadRequest
+        = new RemoteContentRequest(URI.create(preloadUrl));
+
+    String gadgetXml
+        = "<Module>" +
+        "  <ModulePrefs title=\"foo\">" +
+        "    <Preload href=\"" + preloadUrl + "\" authz=\"signed\"/>" +
+        "  </ModulePrefs>" +
+        "  <Content type=\"html\" view=\"v1,v2\">dummy</Content>" +
+        "</Module>";
+    expect(fetcher.fetch(SPEC_REQUEST))
+        .andReturn(new RemoteContent(gadgetXml));
+    replay();
+
+    GadgetContext context = new GadgetContext() {
+      @Override
+      public URI getUrl() {
+        return SPEC_URL;
+      }
+    };
+
+    Gadget gadget = gadgetServer.processGadget(context);
+    assertTrue(gadget.getPreloadMap().size() == 0);
+  }
+
+  public void testSignedPreloadWithToken() throws Exception {
+    String preloadUrl = "http://example.org/preload.txt";;
+    String preloadData = "Preload Data";
+    RemoteContentRequest preloadRequest
+        = new RemoteContentRequest(URI.create(preloadUrl));
+
+    String gadgetXml
+        = "<Module>" +
+        "  <ModulePrefs title=\"foo\">" +
+        "    <Preload href=\"" + preloadUrl + "\" authz=\"signed\"/>" +
+        "  </ModulePrefs>" +
+        "  <Content type=\"html\" view=\"v1,v2\">dummy</Content>" +
+        "</Module>";
+    expect(fetcher.fetch(SPEC_REQUEST))
+        .andReturn(new RemoteContent(gadgetXml));
+    expect(fetcherFactory.getSigningFetcher(BASIC_CONTEXT.getToken()))
+        .andReturn(fetcher);
+    expect(fetcher.fetch(preloadRequest))
+        .andReturn(new RemoteContent(preloadData));
+    replay();
+
+    Gadget gadget = gadgetServer.processGadget(BASIC_CONTEXT);
+    assertTrue(gadget.getPreloadMap().size() == 1);
+  }
+
+
   public void testBlacklistedGadget() throws Exception {
     expect(blacklist.isBlacklisted(eq(SPEC_URL))).andReturn(true);
     replay();

Modified: 
incubator/shindig/trunk/java/gadgets/src/test/java/org/apache/shindig/gadgets/SigningFetcherTest.java
URL: 
http://svn.apache.org/viewvc/incubator/shindig/trunk/java/gadgets/src/test/java/org/apache/shindig/gadgets/SigningFetcherTest.java?rev=653895&r1=653894&r2=653895&view=diff
==============================================================================
--- 
incubator/shindig/trunk/java/gadgets/src/test/java/org/apache/shindig/gadgets/SigningFetcherTest.java
 (original)
+++ 
incubator/shindig/trunk/java/gadgets/src/test/java/org/apache/shindig/gadgets/SigningFetcherTest.java
 Tue May  6 13:01:12 2008
@@ -114,6 +114,28 @@
     assertTrue(contains(queryParams, "xoauth_signature_publickey", "foo"));
   }
 
+  public void testNoSignViewer() throws Exception {
+    RemoteContentRequest unsigned
+        = makeContentRequest("GET", "http://test";, null);
+    unsigned.getOptions().viewerSigned = false;
+    RemoteContentRequest out = signAndInspect(unsigned);
+    List<OAuth.Parameter> queryParams
+        = OAuth.decodeForm(out.getUri().getRawQuery());
+    assertTrue(contains(queryParams, "opensocial_owner_id", "o"));
+    assertFalse(contains(queryParams, "opensocial_viewer_id", "v"));
+  }
+
+  public void testNoSignOwner() throws Exception {
+    RemoteContentRequest unsigned
+        = makeContentRequest("GET", "http://test";, null);
+    unsigned.getOptions().ownerSigned = false;
+    RemoteContentRequest out = signAndInspect(unsigned);
+    List<OAuth.Parameter> queryParams
+        = OAuth.decodeForm(out.getUri().getRawQuery());
+    assertFalse(contains(queryParams, "opensocial_owner_id", "o"));
+    assertTrue(contains(queryParams, "opensocial_viewer_id", "v"));
+  }
+
   public void testTrickyParametersInQuery() throws Exception {
     String tricky = "%6fpensocial_owner_id=gotcha";
     RemoteContentRequest unsigned


Reply via email to