Author: markt
Date: Sat Nov 14 03:47:48 2009
New Revision: 836113

URL: http://svn.apache.org/viewvc?rev=836113&view=rev
Log:
More cookie refactoring
 - new support class for common elements of parsing and writing
 - better consistency between parsing and writing
 - remove unused code
 - reduce visibility of methods where possible
 - auto-switch to v1 for any attribute that might require quoting
 - better names for constants
 - allow v0 cookies to break http spec (disabled by default)
 - update test cases and documentation

Added:
    tomcat/trunk/java/org/apache/tomcat/util/http/CookieSupport.java   (with 
props)
    tomcat/trunk/test/org/apache/tomcat/util/http/TestCookiesAllowHttpSeps.java 
  (with props)
Modified:
    tomcat/trunk/java/org/apache/tomcat/util/http/Cookies.java
    tomcat/trunk/java/org/apache/tomcat/util/http/ServerCookie.java
    tomcat/trunk/test/org/apache/tomcat/util/http/TestCookiesAllowEquals.java
    
tomcat/trunk/test/org/apache/tomcat/util/http/TestCookiesDefaultSysProps.java
    tomcat/trunk/test/org/apache/tomcat/util/http/TestCookiesDisallowEquals.java
    
tomcat/trunk/test/org/apache/tomcat/util/http/TestCookiesNoFwdStrictSysProps.java
    tomcat/trunk/test/org/apache/tomcat/util/http/TestCookiesStrictSysProps.java
    tomcat/trunk/test/org/apache/tomcat/util/http/TestCookiesSwitchSysProps.java
    tomcat/trunk/webapps/docs/config/systemprops.xml

Added: tomcat/trunk/java/org/apache/tomcat/util/http/CookieSupport.java
URL: 
http://svn.apache.org/viewvc/tomcat/trunk/java/org/apache/tomcat/util/http/CookieSupport.java?rev=836113&view=auto
==============================================================================
--- tomcat/trunk/java/org/apache/tomcat/util/http/CookieSupport.java (added)
+++ tomcat/trunk/java/org/apache/tomcat/util/http/CookieSupport.java Sat Nov 14 
03:47:48 2009
@@ -0,0 +1,236 @@
+/*
+ * 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.tomcat.util.http;
+
+
+/**
+ * Static constants for this package.
+ */
+public final class CookieSupport {
+
+       
+    // --------------------------------------------------------------- 
Constants
+    /**
+     * If set to true, we parse cookies strictly according to the servlet,
+     * cookie and HTTP specs by default.
+     */
+    public static final boolean STRICT_SERVLET_COMPLIANCE;
+
+    /**
+     * If true, cookie values are allowed to contain an equals character 
without
+     * being quoted.
+     */
+    public static final boolean ALLOW_EQUALS_IN_VALUE;
+
+    /**
+     * If true, separators that are not explicitly dis-allowed by the v0 cookie
+     * spec but are disallowed by the HTTP spec will be allowed in v0 cookie
+     * names and values. These characters are: \"()/:<=>?...@[\\]{} Note that 
the
+     * inclusion of / depend on the value of {...@link 
#FWD_SLASH_IS_SEPARATOR}.
+     */
+    public static final boolean ALLOW_HTTP_SEPARATORS_IN_V0;
+    
+    /**
+     * If set to false, we don't use the IE6/7 Max-Age/Expires work around.
+     * Default is usually true. If STRICT_SERVLET_COMPLIANCE==true then default
+     * is false. Explicitly setting always takes priority.
+     */
+    public static final boolean ALWAYS_ADD_EXPIRES;
+
+    /**
+     * If set to true, the <code>/</code> character will be treated as a
+     * separator. Default is usually false. If STRICT_SERVLET_COMPLIANCE==true
+     * then default is true. Explicitly setting always takes priority.
+     */
+    public static final boolean FWD_SLASH_IS_SEPARATOR;
+
+    /**
+     * The list of separators that apply to version 0 cookies. To quote the
+     * spec, these are comma, semi-colon and white-space. The HTTP spec
+     * definition of linear white space is [CRLF] 1*( SP | HT )
+     */
+    public static final char[] V0_SEPARATORS = {',', ';', ' ', '\t'};
+    public static final boolean[] V0_SEPARATOR_FLAGS = new boolean[128];
+    
+    /**
+     * The list of separators that apply to version 1 cookies. This may or may
+     * not include '/' depending on the setting of
+     * {...@link #FWD_SLASH_IS_SEPARATOR}.
+     */
+    public static final char[] HTTP_SEPARATORS;
+    public static final boolean[] HTTP_SEPARATOR_FLAGS = new boolean[128];
+    
+    static {
+        STRICT_SERVLET_COMPLIANCE = Boolean.valueOf(System.getProperty(
+                "org.apache.catalina.STRICT_SERVLET_COMPLIANCE",
+                "false")).booleanValue();
+        
+        ALLOW_EQUALS_IN_VALUE = Boolean.valueOf(System.getProperty(
+                
"org.apache.tomcat.util.http.ServerCookie.ALLOW_EQUALS_IN_VALUE",
+                "false")).booleanValue();
+        
+        ALLOW_HTTP_SEPARATORS_IN_V0 = Boolean.valueOf(System.getProperty(
+                
"org.apache.tomcat.util.http.ServerCookie.ALLOW_HTTP_SEPARATORS_IN_V0",
+                "false")).booleanValue();
+        
+        String alwaysAddExpires = System.getProperty(
+        "org.apache.tomcat.util.http.ServerCookie.ALWAYS_ADD_EXPIRES");
+        if (alwaysAddExpires == null) {
+            ALWAYS_ADD_EXPIRES = !STRICT_SERVLET_COMPLIANCE;
+        } else {
+            ALWAYS_ADD_EXPIRES =
+                Boolean.valueOf(alwaysAddExpires).booleanValue();
+        }
+        
+        String  fwdSlashIsSeparator = System.getProperty(
+                
"org.apache.tomcat.util.http.ServerCookie.FWD_SLASH_IS_SEPARATOR");
+        if (fwdSlashIsSeparator == null) {
+            FWD_SLASH_IS_SEPARATOR = STRICT_SERVLET_COMPLIANCE;
+        } else {
+            FWD_SLASH_IS_SEPARATOR =
+                Boolean.valueOf(fwdSlashIsSeparator).booleanValue();
+        }
+        
+        /*
+        Excluding the '/' char by default violates the RFC, but 
+        it looks like a lot of people put '/'
+        in unquoted values: '/': ; //47 
+        '\t':9 ' ':32 '\"':34 '(':40 ')':41 ',':44 ':':58 ';':59 '<':60 
+        '=':61 '>':62 '?':63 '@':64 '[':91 '\\':92 ']':93 '{':123 '}':125
+        */
+        if (CookieSupport.FWD_SLASH_IS_SEPARATOR) {
+            HTTP_SEPARATORS = new char[] { '\t', ' ', '\"', '(', ')', ',', 
'/', 
+                    ':', ';', '<', '=', '>', '?', '@', '[', '\\', ']', '{', 
'}' };
+        } else {
+            HTTP_SEPARATORS = new char[] { '\t', ' ', '\"', '(', ')', ',', 
+                    ':', ';', '<', '=', '>', '?', '@', '[', '\\', ']', '{', 
'}' };
+        }
+        for (int i = 0; i < 128; i++) {
+            V0_SEPARATOR_FLAGS[i] = false;
+            HTTP_SEPARATOR_FLAGS[i] = false;
+        }
+        for (int i = 0; i < V0_SEPARATORS.length; i++) {
+            V0_SEPARATOR_FLAGS[V0_SEPARATORS[i]] = true;
+        }
+        for (int i = 0; i < HTTP_SEPARATORS.length; i++) {
+            HTTP_SEPARATOR_FLAGS[HTTP_SEPARATORS[i]] = true;
+        }
+
+    }
+    
+    // ----------------------------------------------------------------- 
Methods
+
+    /**
+     * Returns true if the byte is a separator as defined by V0 of the cookie
+     * spec.
+     */
+    public static final boolean isV0Separator(final char c) {
+        if (c < 0x20 || c >= 0x7f) {
+            if (c != 0x09) {
+                throw new IllegalArgumentException(
+                        "Control character in cookie value or attribute.");
+            }
+        }
+
+        return V0_SEPARATOR_FLAGS[c];
+    }
+    
+    public static boolean isV0Token(String value) {
+        if( value==null) return false;
+        
+        int i = 0;
+        int len = value.length();
+
+        if (alreadyQuoted(value)) {
+            i++;
+            len--;
+        }
+        
+        for (; i < len; i++) {
+            char c = value.charAt(i);
+
+            if (isV0Separator(c))
+                return true;
+        }
+        return false;
+    }
+
+    /**
+     * Returns true if the byte is a separator as defined by V1 of the cookie
+     * spec, RFC2109.
+     * @throws IllegalArgumentException if a control character was supplied as
+     *         input
+     */
+    public static final boolean isHttpSeparator(final char c) {
+        if (c < 0x20 || c >= 0x7f) {
+            if (c != 0x09) {
+                throw new IllegalArgumentException(
+                        "Control character in cookie value or attribute.");
+            }
+        }
+
+        return HTTP_SEPARATOR_FLAGS[c];
+    }
+
+    public static boolean isHttpToken(String value) {
+        if( value==null) return false;
+        
+        int i = 0;
+        int len = value.length();
+
+        if (alreadyQuoted(value)) {
+            i++;
+            len--;
+        }
+        
+        for (; i < len; i++) {
+            char c = value.charAt(i);
+
+            if (isHttpSeparator(c))
+                return true;
+        }
+        return false;
+    }
+
+    public static boolean containsCTL(String value) {
+        if (value==null) return false;
+        int len = value.length();
+        for (int i = 0; i < len; i++) {
+            char c = value.charAt(i);
+            if (c < 0x20 || c >= 0x7f) {
+                if (c == 0x09)
+                    continue; //allow horizontal tabs
+                return true;
+            }
+        }
+        return false;
+    }
+
+    public static boolean alreadyQuoted (String value) {
+        if (value==null || value.length() < 2) return false;
+        return (value.charAt(0)=='\"' && value.charAt(value.length()-1)=='\"');
+    }
+    
+
+    
+    // ------------------------------------------------------------- 
Constructor
+    private CookieSupport() {
+        // Utility class. Don't allow instances to be created.
+    }
+}

Propchange: tomcat/trunk/java/org/apache/tomcat/util/http/CookieSupport.java
------------------------------------------------------------------------------
    svn:eol-style = native

Modified: tomcat/trunk/java/org/apache/tomcat/util/http/Cookies.java
URL: 
http://svn.apache.org/viewvc/tomcat/trunk/java/org/apache/tomcat/util/http/Cookies.java?rev=836113&r1=836112&r2=836113&view=diff
==============================================================================
--- tomcat/trunk/java/org/apache/tomcat/util/http/Cookies.java (original)
+++ tomcat/trunk/java/org/apache/tomcat/util/http/Cookies.java Sat Nov 14 
03:47:48 2009
@@ -19,7 +19,6 @@
 
 import java.io.PrintWriter;
 import java.io.StringWriter;
-import java.util.StringTokenizer;
 
 import org.apache.tomcat.util.buf.ByteChunk;
 import org.apache.tomcat.util.buf.MessageBytes;
@@ -47,72 +46,6 @@
     MimeHeaders headers;
 
     /**
-     * If set to true, we parse cookies strictly according to the servlet,
-     * cookie and HTTP specs by default.
-     */
-    public static final boolean STRICT_SERVLET_COMPLIANCE;
-
-    /**
-     * If true, cookie values are allowed to contain an equals character 
without
-     * being quoted.
-     */
-    public static final boolean ALLOW_EQUALS_IN_VALUE;
-    
-    /**
-     * If set to true, the <code>/</code> character will be treated as a
-     * separator. Default is usually false. If STRICT_SERVLET_COMPLIANCE==true
-     * then default is true. Explicitly setting always takes priority.
-     */
-    public static final boolean FWD_SLASH_IS_SEPARATOR;
-    
-    /*
-    List of Separator Characters (see isSeparator())
-    */
-    public static final char SEPARATORS[];
-
-    protected static final boolean separators[] = new boolean[128];
-    static {
-        STRICT_SERVLET_COMPLIANCE = Boolean.valueOf(System.getProperty(
-                "org.apache.catalina.STRICT_SERVLET_COMPLIANCE",
-                "false")).booleanValue();
-        
-        ALLOW_EQUALS_IN_VALUE = Boolean.valueOf(System.getProperty(
-                
"org.apache.tomcat.util.http.ServerCookie.ALLOW_EQUALS_IN_VALUE",
-                "false")).booleanValue();
-        
-        String  fwdSlashIsSeparator = System.getProperty(
-                
"org.apache.tomcat.util.http.ServerCookie.FWD_SLASH_IS_SEPARATOR");
-        if (fwdSlashIsSeparator == null) {
-            FWD_SLASH_IS_SEPARATOR = STRICT_SERVLET_COMPLIANCE;
-        } else {
-            FWD_SLASH_IS_SEPARATOR =
-                Boolean.valueOf(fwdSlashIsSeparator).booleanValue();
-        }
-
-        /*
-        Excluding the '/' char by default violates the RFC, but 
-        it looks like a lot of people put '/'
-        in unquoted values: '/': ; //47 
-        '\t':9 ' ':32 '\"':34 '(':40 ')':41 ',':44 ':':58 ';':59 '<':60 
-        '=':61 '>':62 '?':63 '@':64 '[':91 '\\':92 ']':93 '{':123 '}':125
-        */
-        if (FWD_SLASH_IS_SEPARATOR) {
-            SEPARATORS = new char[] { '\t', ' ', '\"', '(', ')', ',', '/', 
-                    ':', ';', '<', '=', '>', '?', '@', '[', '\\', ']', '{', 
'}' };
-        } else {
-            SEPARATORS = new char[] { '\t', ' ', '\"', '(', ')', ',', 
-                    ':', ';', '<', '=', '>', '?', '@', '[', '\\', ']', '{', 
'}' };
-        }
-        
-        for (int i = 0; i < 128; i++) {
-            separators[i] = false;
-        }
-        for (int i = 0; i < SEPARATORS.length; i++) {
-            separators[SEPARATORS[i]] = true;
-        }
-    }
-
-    /**
      *  Construct a new cookie collection, that will extract
      *  the information from headers.
      *
@@ -124,29 +57,6 @@
     }
 
     /**
-     * Construct a new uninitialized cookie collection.
-     * Use {...@link #setHeaders} to initialize.
-     */
-    // [seguin] added so that an empty Cookies object could be
-    // created, have headers set, then recycled.
-    public Cookies() {
-    }
-
-    /**
-     * Set the headers from which cookies will be pulled.
-     * This has the side effect of recycling the object.
-     *
-     * @param headers Cookies are lazy-evaluated and will extract the
-     *     information from the provided headers.
-     */
-    // [seguin] added so that an empty Cookies object could be
-    // created, have headers set, then recycled.
-    public void setHeaders(MimeHeaders headers) {
-        recycle();
-        this.headers=headers;
-    }
-
-    /**
      * Recycle.
      */
     public void recycle() {
@@ -196,7 +106,7 @@
      *  most of the time an existing ServerCookie object is returned.
      *  The caller can set the name/value and attributes for the cookie
      */
-    public ServerCookie addCookie() {
+    private ServerCookie addCookie() {
         if( cookieCount >= scookies.length  ) {
             ServerCookie scookiesTmp[]=new ServerCookie[2*cookieCount];
             System.arraycopy( scookies, 0, scookiesTmp, 0, cookieCount);
@@ -235,24 +145,24 @@
             }
 
             // Uncomment to test the new parsing code
-            if( cookieValue.getType() == MessageBytes.T_BYTES ) {
-                if(log.isDebugEnabled())
-                    log.debug("Cookies: Parsing b[]: " + 
cookieValue.toString());
-                ByteChunk bc=cookieValue.getByteChunk();
-                processCookieHeader( bc.getBytes(),
-                                     bc.getOffset(),
-                                     bc.getLength());
-            } else {
-                if(log.isDebugEnabled())
-                    log.debug("Cookies: Parsing S: " + cookieValue.toString());
-                processCookieHeader( cookieValue.toString() );
-            }
+            if( cookieValue.getType() != MessageBytes.T_BYTES ) {
+                Exception e = new Exception();
+                log.warn("Cookies: Parsing cookie as String. Expected bytes.",
+                        e);
+                cookieValue.toBytes();
+            }
+            if(log.isDebugEnabled())
+                log.debug("Cookies: Parsing b[]: " + cookieValue.toString());
+            ByteChunk bc=cookieValue.getByteChunk();
+            processCookieHeader( bc.getBytes(),
+                                 bc.getOffset(),
+                                 bc.getLength());
             pos++;// search from the next position
         }
     }
 
     // XXX will be refactored soon!
-    public static boolean equals( String s, byte b[], int start, int end) {
+    private static boolean equals( String s, byte b[], int start, int end) {
         int blen = end-start;
         if (b == null || blen != s.length()) {
             return false;
@@ -267,87 +177,12 @@
     }
     
 
-    // ---------------------------------------------------------
-    // -------------------- DEPRECATED, OLD --------------------
-    
-    private void processCookieHeader(  String cookieString )
-    {
-        if(log.isDebugEnabled())
-            log.debug("Cookies: Parsing cookie header " + cookieString);
-        // normal cookie, with a string value.
-        // This is the original code, un-optimized - it shouldn't
-        // happen in normal case
-
-        StringTokenizer tok = new StringTokenizer(cookieString,
-                                                  ";", false);
-        while (tok.hasMoreTokens()) {
-            String token = tok.nextToken();
-            int i = token.indexOf("=");
-            if (i > -1) {
-                
-                // XXX
-                // the trims here are a *hack* -- this should
-                // be more properly fixed to be spec compliant
-                
-                String name = token.substring(0, i).trim();
-                String value = token.substring(i+1, token.length()).trim();
-                // RFC 2109 and bug 
-                value=stripQuote( value );
-                ServerCookie cookie = addCookie();
-                
-                cookie.getName().setString(name);
-                cookie.getValue().setString(value);
-                if(log.isDebugEnabled())
-                    log.debug("Cookies: Add cookie " + name + "=" + value);
-            } else {
-                // we have a bad cookie.... just let it go
-            }
-        }
-    }
-
-    /**
-     *
-     * Strips quotes from the start and end of the cookie string
-     * This conforms to RFC 2965
-     * 
-     * @param value            a <code>String</code> specifying the cookie 
-     *                         value (possibly quoted).
-     *
-     * @see #setValue
-     *
-     */
-    private static String stripQuote( String value )
-    {
-        //        log("Strip quote from " + value );
-        if (value.startsWith("\"") && value.endsWith("\"")) {
-            try {
-                return value.substring(1,value.length()-1);
-            } catch (Exception ex) { 
-            }
-        }
-        return value;
-    }  
-
-   /**
-     * Returns true if the byte is a separator character as
-     * defined in RFC2619. Since this is called often, this
-     * function should be organized with the most probable
-     * outcomes first.
-     * JVK
-     */
-    public static final boolean isSeparator(final byte c) {
-         if (c > 0 && c < 126)
-             return separators[c];
-         else
-             return false;
-    }
-    
     /**
      * Returns true if the byte is a whitespace character as
      * defined in RFC2619
      * JVK
      */
-    public static final boolean isWhiteSpace(final byte c) {
+    private static final boolean isWhiteSpace(final byte c) {
         // This switch statement is slightly slower
         // for my vm than the if statement.
         // Java(TM) 2 Runtime Environment, Standard Edition (build 
1.5.0_07-164)
@@ -370,12 +205,39 @@
     }
 
     /**
+     * Unescapes any double quotes in the given cookie value.
+     *
+     * @param bc The cookie value to modify
+     */
+    private static void unescapeDoubleQuotes(ByteChunk bc) {
+
+        if (bc == null || bc.getLength() == 0 || bc.indexOf('"', 0) == -1) {
+            return;
+        }
+
+        int src = bc.getStart();
+        int end = bc.getEnd();
+        int dest = src;
+        byte[] buffer = bc.getBuffer();
+        
+        while (src < end) {
+            if (buffer[src] == '\\' && src < end && buffer[src+1]  == '"') {
+                src++;
+            }
+            buffer[dest] = buffer[src];
+            dest ++;
+            src ++;
+        }
+        bc.setEnd(dest);
+    }
+
+    /**
      * Parses a cookie header after the initial "Cookie:"
      * [WS][$]token[WS]=[WS](token|QV)[;|,]
      * RFC 2965
      * JVK
      */
-    public final void processCookieHeader(byte bytes[], int off, int len){
+    protected final void processCookieHeader(byte bytes[], int off, int len){
         if( len<=0 || bytes==null ) return;
         int end=off+len;
         int pos=off;
@@ -394,7 +256,10 @@
 
             // Skip whitespace and non-token characters (separators)
             while (pos < end && 
-                   (isSeparator(bytes[pos]) || isWhiteSpace(bytes[pos]))) 
+                   (CookieSupport.isHttpSeparator((char) bytes[pos]) &&
+                           !CookieSupport.ALLOW_HTTP_SEPARATORS_IN_V0 ||
+                    CookieSupport.isV0Separator((char) bytes[pos]) ||
+                    isWhiteSpace(bytes[pos]))) 
                 {pos++; } 
 
             if (pos >= end)
@@ -406,9 +271,9 @@
                 pos++;
             }
 
-            // Get the cookie name. This must be a token            
+            // Get the cookie/attribute name. This must be a token            
             valueEnd = valueStart = nameStart = pos; 
-            pos = nameEnd = getTokenEndPosition(bytes,pos,end);
+            pos = nameEnd = getTokenEndPosition(bytes,pos,end,version,true);
 
             // Skip whitespace
             while (pos < end && isWhiteSpace(bytes[pos])) {pos++; } 
@@ -455,19 +320,23 @@
                     // The position is OK (On a delimiter)
                     break;
                 default:
-                    if (!isSeparator(bytes[pos])) {
+                    if (version == 0 &&
+                                !CookieSupport.isV0Separator((char)bytes[pos]) 
&&
+                                CookieSupport.ALLOW_HTTP_SEPARATORS_IN_V0 ||
+                            !CookieSupport.isHttpSeparator((char)bytes[pos])) {
                         // Token
                         valueStart=pos;
                         // getToken returns the position at the delimeter
                         // or other non-token character
-                        valueEnd=getTokenEndPosition(bytes, valueStart, end);
+                        valueEnd=getTokenEndPosition(bytes, valueStart, end,
+                                version, false);
                         // We need pos to advance
                         pos = valueEnd;
                     } else  {
                         // INVALID COOKIE, advance to next delimiter
                         // The starting character of the cookie value was
                         // not valid.
-                        log.info("Cookies: Invalid cookie." +
+                        log.info("Cookies: Invalid cookie. " +
                                 "Value not a token or quoted value");
                         while (pos < end && bytes[pos] != ';' && 
                                bytes[pos] != ',') 
@@ -503,23 +372,6 @@
             
             pos++;
 
-            /*
-            if (nameEnd <= nameStart || valueEnd < valueStart ) {
-                // Something is wrong, but this may be a case
-                // of having two ';' characters in a row.
-                // log("Cookie name/value does not conform to RFC 2965");
-                // Advance to next delimiter (ignoring everything else)
-                while (pos < end && bytes[pos] != ';' && bytes[pos] != ',') 
-                    { pos++; };
-                pos++;
-                // Make sure no special cookies can be attributed to 
-                // the previous cookie by setting the current cookie
-                // to null
-                sc = null;
-                continue;
-            }
-            */
-
             // All checks passed. Add the cookie, start with the 
             // special avpairs first
             if (isSpecial) {
@@ -557,12 +409,11 @@
                     continue;
                 } 
 
-
+                // v2 cookie attributes - skip them
                 if (equals( "Port", bytes, nameStart, nameEnd)) {
-                    // sc.getPort is not currently implemented.
-                    // sc.getPort().setBytes( bytes,
-                    //                        valueStart,
-                    //                        valueEnd-valueStart );
+                    continue;
+                } 
+                if (equals( "CommentURL", bytes, nameStart, nameEnd)) {
                     continue;
                 } 
 
@@ -580,8 +431,7 @@
                             valueEnd-valueStart);
                     if (isQuoted) {
                         // We know this is a byte value so this is safe
-                        ServerCookie.unescapeDoubleQuotes(
-                                sc.getValue().getByteChunk());
+                        unescapeDoubleQuotes(sc.getValue().getByteChunk());
                     }
                 } else {
                     // Name Only
@@ -597,11 +447,17 @@
      * token, with no separator characters in between.
      * JVK
      */
-    public static final int getTokenEndPosition(byte bytes[], int off, int 
end){
+    private static final int getTokenEndPosition(byte bytes[], int off, int 
end,
+            int version, boolean isName){
         int pos = off;
         while (pos < end &&
-                (!isSeparator(bytes[pos]) ||
-                        bytes[pos]=='=' && ALLOW_EQUALS_IN_VALUE)) {
+                (!CookieSupport.isHttpSeparator((char)bytes[pos]) ||
+                 version == 0 &&
+                        CookieSupport.ALLOW_HTTP_SEPARATORS_IN_V0 &&
+                        bytes[pos] != '=' &&
+                        !CookieSupport.isV0Separator((char)bytes[pos]) ||
+                 !isName && bytes[pos] == '=' &&
+                         CookieSupport.ALLOW_EQUALS_IN_VALUE)) {
             pos++;
         }
         
@@ -615,7 +471,7 @@
      * the position of the end quote. This escapes anything after a '\' char
      * JVK RFC 2616
      */
-    public static final int getQuotedValueEndPosition(byte bytes[], int off, 
int end){
+    private static final int getQuotedValueEndPosition(byte bytes[], int off, 
int end){
         int pos = off;
         while (pos < end) {
             if (bytes[pos] == '"') {

Modified: tomcat/trunk/java/org/apache/tomcat/util/http/ServerCookie.java
URL: 
http://svn.apache.org/viewvc/tomcat/trunk/java/org/apache/tomcat/util/http/ServerCookie.java?rev=836113&r1=836112&r2=836113&view=diff
==============================================================================
--- tomcat/trunk/java/org/apache/tomcat/util/http/ServerCookie.java (original)
+++ tomcat/trunk/java/org/apache/tomcat/util/http/ServerCookie.java Sat Nov 14 
03:47:48 2009
@@ -25,7 +25,6 @@
 import java.util.Locale;
 import java.util.TimeZone;
 
-import org.apache.tomcat.util.buf.ByteChunk;
 import org.apache.tomcat.util.buf.MessageBytes;
 
 
@@ -40,6 +39,7 @@
  */
 public class ServerCookie implements Serializable {
     
+    private static final long serialVersionUID = 1L;
     
     // Version 0 (Netscape) attributes
     private MessageBytes name=MessageBytes.newInstance();
@@ -69,69 +69,21 @@
     };
     private static final String ancientDate;
 
-    /**
-     * If set to true, we parse cookies strictly according to the servlet,
-     * cookie and HTTP specs by default.
-     */
-    public static final boolean STRICT_SERVLET_COMPLIANCE;
-
-    /**
-     * If set to false, we don't use the IE6/7 Max-Age/Expires work around.
-     * Default is usually true. If STRICT_SERVLET_COMPLIANCE==true then default
-     * is false. Explicitly setting always takes priority.
-     */
-    public static final boolean ALWAYS_ADD_EXPIRES;
-
-    /**
-     * If set to true, the <code>/</code> character will be treated as a
-     * separator. Default is usually false. If STRICT_SERVLET_COMPLIANCE==true
-     * then default is true. Explicitly setting always takes priority.
-     */
-    public static final boolean FWD_SLASH_IS_SEPARATOR;
-
-
     static {
         ancientDate = OLD_COOKIE_FORMAT.get().format(new Date(10000));
-        
-        STRICT_SERVLET_COMPLIANCE = Boolean.valueOf(System.getProperty(
-                "org.apache.catalina.STRICT_SERVLET_COMPLIANCE",
-                "false")).booleanValue();
-        
-
-        String alwaysAddExpires = System.getProperty(
-                "org.apache.tomcat.util.http.ServerCookie.ALWAYS_ADD_EXPIRES");
-        if (alwaysAddExpires == null) {
-            ALWAYS_ADD_EXPIRES = !STRICT_SERVLET_COMPLIANCE;
-        } else {
-            ALWAYS_ADD_EXPIRES =
-                Boolean.valueOf(alwaysAddExpires).booleanValue();
-        }
-        
-        String  fwdSlashIsSeparator = System.getProperty(
-                
"org.apache.tomcat.util.http.ServerCookie.FWD_SLASH_IS_SEPARATOR");
-        if (fwdSlashIsSeparator == null) {
-            FWD_SLASH_IS_SEPARATOR = STRICT_SERVLET_COMPLIANCE;
-        } else {
-            FWD_SLASH_IS_SEPARATOR =
-                Boolean.valueOf(fwdSlashIsSeparator).booleanValue();
-        }
-        
-        if (FWD_SLASH_IS_SEPARATOR) {
-            tspecials2 = "()<>@,;:\\\"/[]?={} \t";
-        } else {
-            tspecials2 = "()<>@,;:\\\"[]?={} \t";
-        }
     }
 
-    // Note: Servlet Spec =< 2.5 only refers to Netscape and RFC2109,
+    // Note: Servlet Spec =< 3.0 only refers to Netscape and RFC2109,
     // not RFC2965
 
-    // Version 1 (RFC2965) attributes
-    // TODO Add support for CommentURL
+    // Version 2 (RFC2965) attributes that would need to be added to support
+    // v2 cookies
+    // CommentURL
     // Discard - implied by maxAge <0
-    // TODO Add support for Port
+    // Port
 
     public ServerCookie() {
+        // NOOP
     }
 
     public void recycle() {
@@ -199,98 +151,9 @@
             + getVersion() + " " + getPath() + " " + getDomain();
     }
     
-    private static final String tspecials = ",; ";
-    private static final String tspecials2;
-
-    /*
-     * Tests a string and returns true if the string counts as a
-     * reserved token in the Java language.
-     *
-     * @param value the <code>String</code> to be tested
-     *
-     * @return      <code>true</code> if the <code>String</code> is a reserved
-     *              token; <code>false</code> if it is not
-     */
-    public static boolean isToken(String value) {
-        return isToken(value,null);
-    }
-    
-    public static boolean isToken(String value, String literals) {
-        String tspecials = (literals==null?ServerCookie.tspecials:literals);
-        if( value==null) return true;
-        int len = value.length();
-
-        for (int i = 0; i < len; i++) {
-            char c = value.charAt(i);
-
-            if (tspecials.indexOf(c) != -1)
-                return false;
-        }
-        return true;
-    }
-
-    public static boolean containsCTL(String value, int version) {
-        if( value==null) return false;
-        int len = value.length();
-        for (int i = 0; i < len; i++) {
-            char c = value.charAt(i);
-            if (c < 0x20 || c >= 0x7f) {
-                if (c == 0x09)
-                    continue; //allow horizontal tabs
-                return true;
-            }
-        }
-        return false;
-    }
-
-    public static boolean isToken2(String value) {
-        return isToken2(value,null);
-    }
-
-    public static boolean isToken2(String value, String literals) {
-        String tokens = (literals==null?ServerCookie.tspecials2:literals);
-        if( value==null) return true;
-        int len = value.length();
-
-        for (int i = 0; i < len; i++) {
-            char c = value.charAt(i);
-            if (tokens.indexOf(c) != -1)
-                return false;
-        }
-        return true;
-    }
-
     // -------------------- Cookie parsing tools
 
     
-    /**
-     * Return the header name to set the cookie, based on cookie version.
-     */
-    public String getCookieHeaderName() {
-        return getCookieHeaderName(version);
-    }
-
-    /**
-     * Return the header name to set the cookie, based on cookie version.
-     */
-    public static String getCookieHeaderName(int version) {
-        // TODO Re-enable logging when RFC2965 is implemented
-        // log( (version==1) ? "Set-Cookie2" : "Set-Cookie");
-        if (version == 1) {
-            // XXX RFC2965 not referenced in Servlet Spec
-            // Set-Cookie2 is not supported by Netscape 4, 6, IE 3, 5
-            // Set-Cookie2 is supported by Lynx and Opera
-            // Need to check on later IE and FF releases but for now... 
-            // RFC2109
-            return "Set-Cookie";
-            // return "Set-Cookie2";
-        } else {
-            // Old Netscape
-            return "Set-Cookie";
-        }
-    }
-
-    // TODO RFC2965 fields also need to be passed
     public static void appendCookieValue( StringBuffer headerBuf,
                                           int version,
                                           String name,
@@ -308,41 +171,81 @@
         buf.append("=");
         // Servlet implementation does not check anything else
         
-        version = maybeQuote2(version, buf, value, true);
+        /*
+         * The spec allows some latitude on when to send the version attribute
+         * with a Set-Cookie header. To be nice to clients, we'll make sure the
+         * version attribute is first. That means checking the various things
+         * that can cause us to switch to a v1 cookie first.
+         * 
+         * Note that by checking for tokens we will also throw an exception if 
a
+         * control character is encountered.
+         */
+        // Start by using the version we were asked for
+        int newVersion = version;
         
-        // Spec team clarified setting comment on a v0 cookie switches it to v1
-        if (version == 0 && comment != null) {
-            version = 1;
+        // If it is v0, check if we need to switch
+        if (newVersion == 0 &&
+                (!CookieSupport.ALLOW_HTTP_SEPARATORS_IN_V0 &&
+                 CookieSupport.isHttpToken(value) ||
+                 CookieSupport.ALLOW_HTTP_SEPARATORS_IN_V0 &&
+                 CookieSupport.isV0Token(value))) {
+            // HTTP token in value - need to use v1
+            newVersion = 1;
         }
-
+        
+        if (newVersion == 0 && comment != null) {
+            // Using a comment makes it a v1 cookie
+           newVersion = 1;
+        }
+
+        if (newVersion == 0 &&
+                (!CookieSupport.ALLOW_HTTP_SEPARATORS_IN_V0 &&
+                 CookieSupport.isHttpToken(path) ||
+                 CookieSupport.ALLOW_HTTP_SEPARATORS_IN_V0 &&
+                 CookieSupport.isV0Token(path))) {
+            // HTTP token in path - need to use v1
+            newVersion = 1;
+        }
+
+        if (newVersion == 0 &&
+                (!CookieSupport.ALLOW_HTTP_SEPARATORS_IN_V0 &&
+                 CookieSupport.isHttpToken(domain) ||
+                 CookieSupport.ALLOW_HTTP_SEPARATORS_IN_V0 &&
+                 CookieSupport.isV0Token(domain))) {
+            // HTTP token in domain - need to use v1
+            newVersion = 1;
+        }
+
+        // Now build the cookie header
+        // Value
+        maybeQuote(buf, value);
         // Add version 1 specific information
-        if (version == 1) {
+        if (newVersion == 1) {
             // Version=1 ... required
             buf.append ("; Version=1");
 
             // Comment=comment
             if ( comment!=null ) {
                 buf.append ("; Comment=");
-                maybeQuote2(version, buf, comment);
+                maybeQuote(buf, comment);
             }
         }
         
         // Add domain information, if present
         if (domain!=null) {
             buf.append("; Domain=");
-            maybeQuote2(version, buf, domain);
+            maybeQuote(buf, domain);
         }
 
         // Max-Age=secs ... or use old "Expires" format
-        // TODO RFC2965 Discard
         if (maxAge >= 0) {
-            if (version > 0) {
+            if (newVersion > 0) {
                 buf.append ("; Max-Age=");
                 buf.append (maxAge);
             }
             // IE6, IE7 and possibly other browsers don't understand Max-Age.
             // They do understand Expires, even with V1 cookies!
-            if (version == 0 || ALWAYS_ADD_EXPIRES) {
+            if (newVersion == 0 || CookieSupport.ALWAYS_ADD_EXPIRES) {
                 // Wdy, DD-Mon-YY HH:MM:SS GMT ( Expires Netscape format )
                 buf.append ("; Expires=");
                 // To expire immediately we need to set the time in past
@@ -359,7 +262,7 @@
         // Path=path
         if (path!=null) {
             buf.append ("; Path=");
-            maybeQuote2(version, buf, path);
+            maybeQuote(buf, path);
         }
 
         // Secure
@@ -374,51 +277,28 @@
         headerBuf.append(buf);
     }
 
-    public static boolean alreadyQuoted (String value) {
-        if (value==null || value.length()==0) return false;
-        return (value.charAt(0)=='\"' && value.charAt(value.length()-1)=='\"');
-    }
-    
     /**
-     * Quotes values using rules that vary depending on Cookie version.
-     * @param version
+     * Quotes values if required.
      * @param buf
      * @param value
      */
-    public static int maybeQuote2 (int version, StringBuffer buf, String 
value) {
-        return maybeQuote2(version,buf,value,false);
-    }
-
-    public static int maybeQuote2 (int version, StringBuffer buf, String 
value, boolean allowVersionSwitch) {
-        return maybeQuote2(version,buf,value,null,allowVersionSwitch);
-    }
-
-    public static int maybeQuote2 (int version, StringBuffer buf, String 
value, String literals, boolean allowVersionSwitch) {
+    private static void maybeQuote (StringBuffer buf, String value) {
         if (value==null || value.length()==0) {
             buf.append("\"\"");
-        }else if (containsCTL(value,version)) 
-            throw new IllegalArgumentException("Control character in cookie 
value, consider BASE64 encoding your value");
-        else if (alreadyQuoted(value)) {
+        } else if (CookieSupport.alreadyQuoted(value)) {
             buf.append('"');
             buf.append(escapeDoubleQuotes(value,1,value.length()-1));
             buf.append('"');
-        } else if (allowVersionSwitch && version==0 && !isToken2(value, 
literals)) {
+        } else if (CookieSupport.isHttpToken(value) &&
+                !CookieSupport.ALLOW_HTTP_SEPARATORS_IN_V0 ||
+                CookieSupport.isV0Token(value) &&
+                CookieSupport.ALLOW_HTTP_SEPARATORS_IN_V0) {
             buf.append('"');
             buf.append(escapeDoubleQuotes(value,0,value.length()));
             buf.append('"');
-            version = 1;
-        } else if (version==0 && !isToken(value,literals)) {
-            buf.append('"');
-            buf.append(escapeDoubleQuotes(value,0,value.length()));
-            buf.append('"');
-        } else if (version==1 && !isToken2(value,literals)) {
-            buf.append('"');
-            buf.append(escapeDoubleQuotes(value,0,value.length()));
-            buf.append('"');
-        }else {
+        } else {
             buf.append(value);
         }
-        return version;
     }
 
 
@@ -453,31 +333,5 @@
         return b.toString();
     }
 
-    /**
-     * Unescapes any double quotes in the given cookie value.
-     *
-     * @param bc The cookie value to modify
-     */
-    public static void unescapeDoubleQuotes(ByteChunk bc) {
-
-        if (bc == null || bc.getLength() == 0 || bc.indexOf('"', 0) == -1) {
-            return;
-        }
-
-        int src = bc.getStart();
-        int end = bc.getEnd();
-        int dest = src;
-        byte[] buffer = bc.getBuffer();
-        
-        while (src < end) {
-            if (buffer[src] == '\\' && src < end && buffer[src+1]  == '"') {
-                src++;
-            }
-            buffer[dest] = buffer[src];
-            dest ++;
-            src ++;
-        }
-        bc.setEnd(dest);
-    }
 }
 

Modified: 
tomcat/trunk/test/org/apache/tomcat/util/http/TestCookiesAllowEquals.java
URL: 
http://svn.apache.org/viewvc/tomcat/trunk/test/org/apache/tomcat/util/http/TestCookiesAllowEquals.java?rev=836113&r1=836112&r2=836113&view=diff
==============================================================================
--- tomcat/trunk/test/org/apache/tomcat/util/http/TestCookiesAllowEquals.java 
(original)
+++ tomcat/trunk/test/org/apache/tomcat/util/http/TestCookiesAllowEquals.java 
Sat Nov 14 03:47:48 2009
@@ -68,7 +68,7 @@
             disconnect();
             reset();
             tomcat.stop();
-            assertTrue(response.contains(COOKIE_WITH_EQUALS));
+            assertEquals(COOKIE_WITH_EQUALS, response);
         }
         
         @Override

Added: 
tomcat/trunk/test/org/apache/tomcat/util/http/TestCookiesAllowHttpSeps.java
URL: 
http://svn.apache.org/viewvc/tomcat/trunk/test/org/apache/tomcat/util/http/TestCookiesAllowHttpSeps.java?rev=836113&view=auto
==============================================================================
--- tomcat/trunk/test/org/apache/tomcat/util/http/TestCookiesAllowHttpSeps.java 
(added)
+++ tomcat/trunk/test/org/apache/tomcat/util/http/TestCookiesAllowHttpSeps.java 
Sat Nov 14 03:47:48 2009
@@ -0,0 +1,99 @@
+/*
+ *  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.tomcat.util.http;
+
+import java.io.IOException;
+
+import javax.servlet.ServletException;
+import javax.servlet.http.Cookie;
+import javax.servlet.http.HttpServlet;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import org.apache.catalina.core.StandardContext;
+import org.apache.catalina.startup.SimpleHttpClient;
+import org.apache.catalina.startup.TomcatBaseTest;
+import org.apache.catalina.startup.Tomcat;
+
+public class TestCookiesAllowHttpSeps extends TomcatBaseTest{
+
+    private static final String COOKIE_WITH_SEPS = "name=val(ue";
+    
+    public void testWithHttpSep() throws Exception {
+        System.setProperty(
+                
"org.apache.tomcat.util.http.ServerCookie.ALLOW_HTTP_SEPARATORS_IN_V0",
+                "true");
+
+        TestCookieHttpSepClient client = new TestCookieHttpSepClient();
+        client.doRequest();
+    }
+    
+    private class TestCookieHttpSepClient extends SimpleHttpClient {
+
+
+        private void doRequest() throws Exception {
+            Tomcat tomcat = getTomcatInstance();
+            StandardContext root = tomcat.addContext("", TEMP_DIR);
+            Tomcat.addServlet(root, "Simple", new SimpleServlet());
+            root.addServletMapping("/test", "Simple");
+            
+            tomcat.start();
+            // Open connection
+            setPort(tomcat.getConnector().getPort());
+            connect();
+            
+            String[] request = new String[1];
+            request[0] =
+                "GET /test HTTP/1.0" + CRLF +
+                "Cookie: " + COOKIE_WITH_SEPS + CRLF + CRLF;
+            setRequest(request);
+            processRequest(true); // blocks until response has been read
+            String response = getResponseBody();
+            
+            // Close the connection
+            disconnect();
+            reset();
+            tomcat.stop();
+            assertEquals(COOKIE_WITH_SEPS, response);
+        }
+        
+        @Override
+        public boolean isResponseBodyOK() {
+            return true;
+        }
+        
+    }
+    
+    
+    private static class SimpleServlet extends HttpServlet {
+
+        private static final long serialVersionUID = 1L;
+
+        @Override
+        protected void service(HttpServletRequest req, HttpServletResponse 
resp)
+        throws ServletException, IOException {
+            Cookie cookies[] = req.getCookies();
+            for (Cookie cookie : cookies) {
+                resp.getWriter().write(cookie.getName() + "=" +
+                        cookie.getValue() + "\n");
+            }
+            resp.flushBuffer();
+        }
+        
+    }
+    
+}

Propchange: 
tomcat/trunk/test/org/apache/tomcat/util/http/TestCookiesAllowHttpSeps.java
------------------------------------------------------------------------------
    svn:eol-style = native

Modified: 
tomcat/trunk/test/org/apache/tomcat/util/http/TestCookiesDefaultSysProps.java
URL: 
http://svn.apache.org/viewvc/tomcat/trunk/test/org/apache/tomcat/util/http/TestCookiesDefaultSysProps.java?rev=836113&r1=836112&r2=836113&view=diff
==============================================================================
--- 
tomcat/trunk/test/org/apache/tomcat/util/http/TestCookiesDefaultSysProps.java 
(original)
+++ 
tomcat/trunk/test/org/apache/tomcat/util/http/TestCookiesDefaultSysProps.java 
Sat Nov 14 03:47:48 2009
@@ -59,9 +59,7 @@
         getUrl("http://localhost:"; + getPort() + "/switch", res, headers);
         List<String> cookieHeaders = headers.get("Set-Cookie");
         for (String cookieHeader : cookieHeaders) {
-            if (cookieHeader.contains("name=")) {
-                assertTrue(cookieHeader.contains("name=\"val?ue\";"));
-            }
+            assertEquals("name=\"val?ue\"; Version=1", cookieHeader);
         }
 
     }

Modified: 
tomcat/trunk/test/org/apache/tomcat/util/http/TestCookiesDisallowEquals.java
URL: 
http://svn.apache.org/viewvc/tomcat/trunk/test/org/apache/tomcat/util/http/TestCookiesDisallowEquals.java?rev=836113&r1=836112&r2=836113&view=diff
==============================================================================
--- 
tomcat/trunk/test/org/apache/tomcat/util/http/TestCookiesDisallowEquals.java 
(original)
+++ 
tomcat/trunk/test/org/apache/tomcat/util/http/TestCookiesDisallowEquals.java 
Sat Nov 14 03:47:48 2009
@@ -65,7 +65,7 @@
             disconnect();
             reset();
             tomcat.stop();
-            assertTrue(response.contains(COOKIE_TRUNCATED));
+            assertEquals(COOKIE_TRUNCATED, response);
         }
         
         @Override

Modified: 
tomcat/trunk/test/org/apache/tomcat/util/http/TestCookiesNoFwdStrictSysProps.java
URL: 
http://svn.apache.org/viewvc/tomcat/trunk/test/org/apache/tomcat/util/http/TestCookiesNoFwdStrictSysProps.java?rev=836113&r1=836112&r2=836113&view=diff
==============================================================================
--- 
tomcat/trunk/test/org/apache/tomcat/util/http/TestCookiesNoFwdStrictSysProps.java
 (original)
+++ 
tomcat/trunk/test/org/apache/tomcat/util/http/TestCookiesNoFwdStrictSysProps.java
 Sat Nov 14 03:47:48 2009
@@ -51,7 +51,7 @@
         res = getUrl("http://localhost:"; + getPort() + "/invalidFwd");
         assertEquals("Cookie name ok", res.toString());
         res = getUrl("http://localhost:"; + getPort() + "/invalidStrict");
-        assertEquals("Cookie name fail", res.toString());
+        assertEquals("Cookie name ok", res.toString()); // Will auto switch
         res = getUrl("http://localhost:"; + getPort() + "/valid");
         assertEquals("Cookie name ok", res.toString());
 

Modified: 
tomcat/trunk/test/org/apache/tomcat/util/http/TestCookiesStrictSysProps.java
URL: 
http://svn.apache.org/viewvc/tomcat/trunk/test/org/apache/tomcat/util/http/TestCookiesStrictSysProps.java?rev=836113&r1=836112&r2=836113&view=diff
==============================================================================
--- 
tomcat/trunk/test/org/apache/tomcat/util/http/TestCookiesStrictSysProps.java 
(original)
+++ 
tomcat/trunk/test/org/apache/tomcat/util/http/TestCookiesStrictSysProps.java 
Sat Nov 14 03:47:48 2009
@@ -51,9 +51,9 @@
         res = getUrl("http://localhost:"; + getPort() + "/blank");
         assertEquals("Cookie name fail", res.toString());
         res = getUrl("http://localhost:"; + getPort() + "/invalidFwd");
-        assertEquals("Cookie name fail", res.toString());
+        assertEquals("Cookie name ok", res.toString()); // Will auto-switch
         res = getUrl("http://localhost:"; + getPort() + "/invalidStrict");
-        assertEquals("Cookie name fail", res.toString());
+        assertEquals("Cookie name ok", res.toString()); // Will auto-switch
         res = getUrl("http://localhost:"; + getPort() + "/valid");
         assertEquals("Cookie name ok", res.toString());
         
@@ -62,9 +62,7 @@
         getUrl("http://localhost:"; + getPort() + "/switch", res, headers);
         List<String> cookieHeaders = headers.get("Set-Cookie");
         for (String cookieHeader : cookieHeaders) {
-            if (cookieHeader.contains("name=")) {
-                assertTrue(cookieHeader.contains("name=val?ue"));
-            }
+            assertEquals("name=\"val?ue\"; Version=1", cookieHeader);
         }
         
     }

Modified: 
tomcat/trunk/test/org/apache/tomcat/util/http/TestCookiesSwitchSysProps.java
URL: 
http://svn.apache.org/viewvc/tomcat/trunk/test/org/apache/tomcat/util/http/TestCookiesSwitchSysProps.java?rev=836113&r1=836112&r2=836113&view=diff
==============================================================================
--- 
tomcat/trunk/test/org/apache/tomcat/util/http/TestCookiesSwitchSysProps.java 
(original)
+++ 
tomcat/trunk/test/org/apache/tomcat/util/http/TestCookiesSwitchSysProps.java 
Sat Nov 14 03:47:48 2009
@@ -54,9 +54,9 @@
         res = getUrl("http://localhost:"; + getPort() + "/blank");
         assertEquals("Cookie name fail", res.toString());
         res = getUrl("http://localhost:"; + getPort() + "/invalidFwd");
-        assertEquals("Cookie name fail", res.toString());
+        assertEquals("Cookie name ok", res.toString()); // Will auto-switch
         res = getUrl("http://localhost:"; + getPort() + "/invalidStrict");
-        assertEquals("Cookie name fail", res.toString());
+        assertEquals("Cookie name ok", res.toString()); // Will auto-switch
         res = getUrl("http://localhost:"; + getPort() + "/valid");
         assertEquals("Cookie name ok", res.toString());
 

Modified: tomcat/trunk/webapps/docs/config/systemprops.xml
URL: 
http://svn.apache.org/viewvc/tomcat/trunk/webapps/docs/config/systemprops.xml?rev=836113&r1=836112&r2=836113&view=diff
==============================================================================
--- tomcat/trunk/webapps/docs/config/systemprops.xml (original)
+++ tomcat/trunk/webapps/docs/config/systemprops.xml Sat Nov 14 03:47:48 2009
@@ -248,8 +248,8 @@
       </ul>
       </p>
       <p>Note that changing a number of the above defaults is likely to break
-      the majority of systems as a number of browsers are unable to correctly
-      handle the cookie headers that result from a strict adherence to the
+      the majority of systems as some browsers are unable to correctly handle
+      the cookie headers that result from a strict adherence to the
       specifications. Defaults, regardless of whether or not they have been
       changed by setting
       <code>org.apache.catalina.STRICT_SERVLET_COMPLIANCE</code> can always be
@@ -277,6 +277,13 @@
     </property>
 
     <property
+    name="org.apache.tomcat.util.http. 
ServerCookie.ALLOW_HTTP_SEPARATORS_IN_V0">
+      <p>If this is <code>true</code> Tomcat will allow HTTP separators in
+      cookie names and values. If not specified, the default specification
+      compliant value of <code>false</code> will be used.</p>
+    </property>
+
+    <property
     name="org.apache.tomcat.util.http. ServerCookie.ALWAYS_ADD_EXPIRES">
       <p>If this is <code>true</code> Tomcat will always add an expires
       parameter to a SetCookie header even for cookies with version greater 
than
@@ -294,7 +301,8 @@
       be treated as a separator. Note that this character is frequently used in
       cookie path attributes and some browsers will fail to process a cookie if
       the path attribute is quoted as is required by a strict adherence to the
-      specifications. If not specified, the default value will be used. If
+      specifications. This is highly likely to break session tracking using
+      cookies. If not specified, the default value will be used. If
       <code>org.apache.catalina.STRICT_SERVLET_COMPLIANCE</code> is set to
       <code>true</code>, the default of this setting will be <code>true</code>,
       else the default value will be <code>false</code>.</p>



---------------------------------------------------------------------
To unsubscribe, e-mail: dev-unsubscr...@tomcat.apache.org
For additional commands, e-mail: dev-h...@tomcat.apache.org

Reply via email to