Author: kpro...@google.com
Date: Wed May 20 06:17:16 2009
New Revision: 5437

Modified:
    trunk/user/src/com/google/gwt/user/client/Cookies.java
    trunk/user/test/com/google/gwt/user/client/CookieTest.java

Log:
Addresses public issue 3566: Offer option to not decode cookies
Addresses public issue 1574: RFE: Provide a Cookies.removeCookie(name,  
path) method

Patch by: kprobst
Review by: jat, jwg



Modified: trunk/user/src/com/google/gwt/user/client/Cookies.java
==============================================================================
--- trunk/user/src/com/google/gwt/user/client/Cookies.java      (original)
+++ trunk/user/src/com/google/gwt/user/client/Cookies.java      Wed May 20  
06:17:16 2009
@@ -40,10 +40,34 @@
    static String rawCookies;

    /**
+   * Flag that indicates whether cookies should be URIencoded (when set)  
and
+   * URIdecoded (when retrieved). Defaults to URIencoding.
+   */
+  private static boolean uriEncoding = true;
+
+  /**
+   * Gets the URIencode flag
+   */
+  public static boolean getUriEncode() {
+    return uriEncoding;
+  }
+
+  /**
+   * Updates the URIencode flag and empties the cached cookies set
+   */
+  public static void setUriEncode(boolean encode) {
+    if (encode != uriEncoding) {
+      uriEncoding = encode;
+      cachedCookies = null;
+    }
+  }
+
+  /**
     * Gets the cookie associated with the given name.
     *
     * @param name the name of the cookie to be retrieved
-   * @return the cookie's value, or <code>null</code> if the cookie  
doesn't exist
+   * @return the cookie's value, or <code>null</code> if the cookie doesn't
+   *         exist
     */
    public static String getCookie(String name) {
      Map<String, String> cookiesMap = ensureCookies();
@@ -64,9 +88,40 @@
     *
     * @param name the name of the cookie to be removed
     */
-  public static native void removeCookie(String name) /*-{
-    $doc.cookie = name + "=;expires=Fri, 02-Jan-1970 00:00:00 GMT";
-  }-*/;
+  public static void removeCookie(String name) {
+    if (uriEncoding) {
+      uriEncode(name);
+    }
+    removeCookieNative(name);
+  }
+
+  /**
+   * Native method to remove a cookie
+   */
+  private static native void removeCookieNative(String name) /*-{
+        $doc.cookie = name + "=;expires=Fri, 02-Jan-1970 00:00:00 GMT";
+      }-*/;
+
+  /**
+   * Removes the cookie associated with the given name.
+   *
+   * @param name the name of the cookie to be removed
+   * @param path the path to be associated with this cookie (which should  
match
+   *          the path given in {...@link #setCookie})
+   */
+  public static void removeCookie(String name, String path) {
+    if (uriEncoding) {
+      uriEncode(name);
+    }
+    removeCookieNative(name, path);
+  }
+
+  /**
+   * Native method to remove a cookie with a path
+   */
+  public static native void removeCookieNative(String name, String path)  
/*-{
+       $doc.cookie = name + "=;path=" + path + ";expires=Fri, 02-Jan-1970  
00:00:00 GMT";
+     }-*/;

    /**
     * Sets a cookie. The cookie will expire when the current browser  
session is
@@ -76,7 +131,7 @@
     * @param value the cookie's value
     */
    public static void setCookie(String name, String value) {
-    setCookieImpl(name, value, 0, null, null, false);
+    setCookie(name, value, null, null, null, false);
    }

    /**
@@ -91,49 +146,97 @@
    }

    /**
-   * Sets a cookie.
+   * Sets a cookie. If uriEncoding is false, it checks the validity of  
name and
+   * value. Name: Must conform to RFC 2965. Not allowed: = , ; white  
space. Also
+   * can't begin with $. Value: No = or ;
     *
     * @param name the cookie's name
     * @param value the cookie's value
     * @param expires when the cookie expires
     * @param domain the domain to be associated with this cookie
     * @param path the path to be associated with this cookie
-   * @param secure <code>true</code> to make this a secure cookie (that is,
-   * only accessible over an SSL connection)
+   * @param secure <code>true</code> to make this a secure cookie (that  
is, only
+   *          accessible over an SSL connection)
     */
    public static void setCookie(String name, String value, Date expires,
        String domain, String path, boolean secure) {
-    setCookieImpl(name, value, (expires == null) ? 0 : expires.getTime(),  
domain, path, secure);
+    if (uriEncoding) {
+      uriEncode(name);
+      uriEncode(value);
+    } else if (!isValidCookieName(name) || !isValidCookieValue(value)) {
+      throw new IllegalArgumentException("Illegal cookie format.");
+    }
+    setCookieImpl(name, value, (expires == null) ? 0 : expires.getTime(),
+        domain, path, secure);
+  }
+
+  /*
+   * Checks whether a cookie name is valid: can't contain =;, or  
whitespace.
+   * Can't begin with $.
+   *
+   * @param name the cookie's name
+   */
+  private static boolean isValidCookieName(String name) {
+    if (uriEncoding) {
+      // check not necessary
+      return true;
+    } else if (name.contains("=") || name.contains(";") ||  
name.contains(",")
+        || name.startsWith("$") || name.matches(".*\\s+.*")) {
+      return false;
+    } else
+      return true;
    }

+  /*
+   * Checks whether a cookie value is valid: can't contain = or ;
+   *
+   * @param value the cookie's value
+   */
+  private static boolean isValidCookieValue(String value) {
+    if (uriEncoding) {
+      // check not necessary
+      return true;
+    }
+    if (value.contains("=") || value.contains(";")) {
+      return false;
+    } else
+      return true;
+  }
+
+  private static native String uriEncode(String s) /*-{
+       return encodeURIComponent(s);
+     }-*/;
+
    static native void loadCookies(HashMap<String, String> m) /*-{
-    var docCookie = $doc.cookie;
-    if (docCookie && docCookie != '') {
-      var crumbs = docCookie.split('; ');
-      for (var i = 0; i < crumbs.length; ++i) {
-        var name, value;
-        var eqIdx = crumbs[i].indexOf('=');
-        if (eqIdx == -1) {
-          name = crumbs[i];
-          value = '';
-        } else {
-          name = crumbs[i].substring(0, eqIdx);
-          value = crumbs[i].substring(eqIdx + 1);
+        var docCookie = $doc.cookie;
+        if (docCookie && docCookie != '') {
+          var crumbs = docCookie.split('; ');
+          for (var i = 0; i < crumbs.length; ++i) {
+            var name, value;
+            var eqIdx = crumbs[i].indexOf('=');
+            if (eqIdx == -1) {
+              name = crumbs[i];
+              value = '';
+            } else {
+              name = crumbs[i].substring(0, eqIdx);
+              value = crumbs[i].substring(eqIdx + 1);
+            }
+            if (@com.google.gwt.user.client.Cookies::uriEncoding) {
+              try {
+                name = decodeURIComponent(name);
+              } catch (e) {
+                // ignore error, keep undecoded name
+              }
+              try {
+                value = decodeURIComponent(value);
+              } catch (e) {
+                // ignore error, keep undecoded value
+              }
+            }
+             
m...@java.util.map::put(Ljava/lang/Object;Ljava/lang/Object;)(name,value);
+          }
          }
-        try {
-          name = decodeURIComponent(name);
-        } catch (e) {
-          // ignore error, keep undecoded name
-        }
-        try {
-          value = decodeURIComponent(value);
-        } catch (e) {
-          // ignore error, keep undecoded value
-        }
-         
m...@java.util.map::put(Ljava/lang/Object;Ljava/lang/Object;)(name,value);
-      }
-    }
-  }-*/;
+      }-*/;

    private static HashMap<String, String> ensureCookies() {
      if (cachedCookies == null || needsRefresh()) {
@@ -144,31 +247,31 @@
    }

    private static native boolean needsRefresh() /*-{
-    var docCookie = $doc.cookie;
-
-    // Check to see if cached cookies need to be invalidated.
-    if (docCookie != @com.google.gwt.user.client.Cookies::rawCookies) {
-      @com.google.gwt.user.client.Cookies::rawCookies = docCookie;
-      return true;
-    } else {
-      return false;
-    }
-  }-*/;
+        var docCookie = $doc.cookie;
+
+        // Check to see if cached cookies need to be invalidated.
+        if (docCookie != @com.google.gwt.user.client.Cookies::rawCookies) {
+          @com.google.gwt.user.client.Cookies::rawCookies = docCookie;
+          return true;
+        } else {
+          return false;
+        }
+      }-*/;

    private static native void setCookieImpl(String name, String value,
        double expires, String domain, String path, boolean secure) /*-{
-    var c = encodeURIComponent(name) + '=' + encodeURIComponent(value);
-    if ( expires )
-      c += ';expires=' + (new Date(expires)).toGMTString();
-    if (domain)
-      c += ';domain=' + domain;
-    if (path)
-      c += ';path=' + path;
-    if (secure)
-      c += ';secure';
+        var c = name + '=' + value;
+        if ( expires )
+          c += ';expires=' + (new Date(expires)).toGMTString();
+        if (domain)
+          c += ';domain=' + domain;
+        if (path)
+          c += ';path=' + path;
+        if (secure)
+          c += ';secure';

-    $doc.cookie = c;
-  }-*/;
+        $doc.cookie = c;
+      }-*/;

    private Cookies() {
    }

Modified: trunk/user/test/com/google/gwt/user/client/CookieTest.java
==============================================================================
--- trunk/user/test/com/google/gwt/user/client/CookieTest.java  (original)
+++ trunk/user/test/com/google/gwt/user/client/CookieTest.java  Wed May 20  
06:17:16 2009
@@ -79,7 +79,7 @@
          assertEquals(Cookies.getCookie("shouldNotExpire"), "forever");
          Cookies.removeCookie("shouldNotExpire");
          assertNull(Cookies.getCookie("shouldNotExpire"));
-
+
          // Finish the test
          finishTest();
        }
@@ -87,21 +87,21 @@
      timer.schedule(5010);
      delayTestFinish(6 * 1000);
    }
-
+
    /**
     * Test that removing cookies works correctly.
     */
    public void testRemoveCookie() {
      // First clear all cookies
      clearCookies();
-
+
      // Set a few cookies
      Cookies.setCookie("test1", "value1");
      Cookies.setCookie("test2", "value2");
      Cookies.setCookie("test3", "value3");
      Collection<String> cookies = Cookies.getCookieNames();
      assertEquals(3, cookies.size());
-
+
      // Remove a cookie
      Cookies.removeCookie("test2");
      assertEquals("value1", Cookies.getCookie("test1"));
@@ -121,15 +121,147 @@
      assertEquals(null, Cookies.getCookie("test3"));
      cookies = Cookies.getCookieNames();
      assertEquals(0, cookies.size());
+
+    // Add/remove URI encoded cookies
+    Cookies.setCookie("test1-test1", "value1 value1");
+    Cookies.removeCookie("test1-test1");
+    cookies = Cookies.getCookieNames();
+    assertEquals(0, cookies.size());
+
+    // Add/remove cookies that are not URI encoded
+    Cookies.setUriEncode(false);
+    Cookies.setCookie("test1+test1", "value1+value1");
+    Cookies.removeCookie("test1+test1");
+    cookies = Cookies.getCookieNames();
+    assertEquals(0, cookies.size());
+
+    // Make sure unencoded cookies with bogus format are not added
+    try {
+      Cookies.setCookie("test1=test1", "value1");
+      fail("Should have thrown an IllegalArgumentException for bad cookie  
format.");
+    } catch (IllegalArgumentException e) {
+      // Success.
+    }
+    try {
+      Cookies.setCookie("test1;test1", "value1");
+      fail("Should have thrown an IllegalArgumentException for bad cookie  
format.");
+    } catch (IllegalArgumentException e) {
+      // Success.
+    }
+    try {
+      Cookies.setCookie("test1,test1", "value1");
+      fail("Should have thrown an IllegalArgumentException for bad cookie  
format.");
+    } catch (IllegalArgumentException e) {
+      // Success.
+    }
+    try {
+      Cookies.setCookie("test1 test1", "value1");
+      fail("Should have thrown an IllegalArgumentException for bad cookie  
format.");
+    } catch (IllegalArgumentException e) {
+      // Success.
+    }
+    try {
+      Cookies.setCookie("$test1", "value1");
+      fail("Should have thrown an IllegalArgumentException for bad cookie  
format.");
+    } catch (IllegalArgumentException e) {
+      // Success.
+    }
+    try {
+      Cookies.setCookie("test1", "value1;value1");
+      fail("Should have thrown an IllegalArgumentException for bad cookie  
format.");
+    } catch (IllegalArgumentException e) {
+      // Success.
+    }
+    try {
+      Cookies.setCookie("test1", "value1=value1");
+      fail("Should have thrown an IllegalArgumentException for bad cookie  
format.");
+    } catch (IllegalArgumentException e) {
+      // Success.
+    }
+
    }
-
+
    /**
     * Clear out all existing cookies.
     */
-  private void clearCookies() {
+  private void clearCookies() {
      Collection<String> cookies = Cookies.getCookieNames();
      for (String cookie : cookies) {
        Cookies.removeCookie(cookie);
      }
+  }
+
+  /**
+   * Test that removing cookies with a path works correctly.
+   *
+   * Note that we do not verify failure to remove a cookie set with a path  
but
+   * removed without one as browser behavior differs.
+   */
+  public void testRemoveCookiePath() {
+    // First clear all cookies
+    clearCookies();
+
+    // Test first without UriEncoding
+    Cookies.setUriEncode(false);
+
+    // Set a few cookies
+    Cookies.setCookie("test1+test1", "value1", null, null, "/", false);
+    Cookies.setCookie("test2", "value2+value2", null, null, "/", false);
+    Cookies.setCookie("test3", "value3", null, null, "/", false);
+    Collection<String> cookies = Cookies.getCookieNames();
+    assertEquals(3, cookies.size());
+
+    // Remove a cookie
+    Cookies.removeCookie("test2", "/");
+    assertEquals("value1", Cookies.getCookie("test1+test1"));
+    assertEquals(null, Cookies.getCookie("test2"));
+    assertEquals("value3", Cookies.getCookie("test3"));
+
+    // Remove another cookie
+    Cookies.removeCookie("test1+test1", "/");
+    assertEquals(null, Cookies.getCookie("test1+test1"));
+    assertEquals(null, Cookies.getCookie("test2"));
+    assertEquals("value3", Cookies.getCookie("test3"));
+
+    // Remove last cookie
+    Cookies.removeCookie("test3", "/");
+    assertEquals(null, Cookies.getCookie("test1+test1"));
+    assertEquals(null, Cookies.getCookie("test2"));
+    assertEquals(null, Cookies.getCookie("test3"));
+    cookies = Cookies.getCookieNames();
+    assertEquals(0, cookies.size());
+
+    // First clear all cookies
+    clearCookies();
+
+    // Test with UriEncoding
+    Cookies.setUriEncode(true);
+
+    // Set a few cookies
+    Cookies.setCookie("test1+test1", "value1", null, null, "/", false);
+    Cookies.setCookie("test2", "value2+value2", null, null, "/", false);
+    Cookies.setCookie("test3", "value3", null, null, "/", false);
+    cookies = Cookies.getCookieNames();
+    assertEquals(3, cookies.size());
+
+    // Remove a cookie
+    Cookies.removeCookie("test2", "/");
+    assertEquals("value1", Cookies.getCookie("test1+test1"));
+    assertEquals(null, Cookies.getCookie("test2"));
+    assertEquals("value3", Cookies.getCookie("test3"));
+
+    // Remove another cookie
+    Cookies.removeCookie("test1+test1", "/");
+    assertEquals(null, Cookies.getCookie("test1+test1"));
+    assertEquals(null, Cookies.getCookie("test2"));
+    assertEquals("value3", Cookies.getCookie("test3"));
+
+    // Remove last cookie
+    Cookies.removeCookie("test3", "/");
+    assertEquals(null, Cookies.getCookie("test1+test1"));
+    assertEquals(null, Cookies.getCookie("test2"));
+    assertEquals(null, Cookies.getCookie("test3"));
+    cookies = Cookies.getCookieNames();
+    assertEquals(0, cookies.size());
    }
  }

--~--~---------~--~----~------------~-------~--~----~
http://groups.google.com/group/Google-Web-Toolkit-Contributors
-~----------~----~----~----~------~----~------~--~---

Reply via email to