marcsaeg    02/03/15 14:51:08

  Modified:    httpclient/src/java/org/apache/commons/httpclient
                        Cookie.java
  Log:
  Several changes to improve compliance with RFC 2109 and The Netscape
  Cookie spec-like-thing (http://www.netscape.com/newsref/std/cookie_spec.html).
  
  1) The $Path and $Domain cookie header attributes are only sent if the
  cookies had a Path or Domain specified in the original set-cookie headers.
  
  2) The $Version attribute is now set correctly on the cookie header.  That
  is, it is set to 0 for cookies that didn't have a Version attribute on the
  original set-cookie header (i.e.  Netscape cookies).
  
  3) Cookies with no domain or path specified (i.e.  Cookie.getPath() or
  Cookie.getDomain() return null) are no longer added to *any* cookie
  headers.
  
  4) Cookie.createCookieHeader() and Cookie.parse() now throw an
  IllegalArgumentException if the passed in domain or path are null.
  
  5) Cookie.parse() now properly sets new cookies' default path based on
  the passed in request URI ( RFC 2109/4.3.1).
  
  Revision  Changes    Path
  1.16      +167 -22   
jakarta-commons/httpclient/src/java/org/apache/commons/httpclient/Cookie.java
  
  Index: Cookie.java
  ===================================================================
  RCS file: 
/home/cvs/jakarta-commons/httpclient/src/java/org/apache/commons/httpclient/Cookie.java,v
  retrieving revision 1.15
  retrieving revision 1.16
  diff -u -r1.15 -r1.16
  --- Cookie.java       24 Feb 2002 17:28:53 -0000      1.15
  +++ Cookie.java       15 Mar 2002 22:51:08 -0000      1.16
  @@ -1,7 +1,7 @@
   /*
  - * $Header: 
/home/cvs/jakarta-commons/httpclient/src/java/org/apache/commons/httpclient/Cookie.java,v
 1.15 2002/02/24 17:28:53 marcsaeg Exp $
  - * $Revision: 1.15 $
  - * $Date: 2002/02/24 17:28:53 $
  + * $Header: 
/home/cvs/jakarta-commons/httpclient/src/java/org/apache/commons/httpclient/Cookie.java,v
 1.16 2002/03/15 22:51:08 marcsaeg Exp $
  + * $Revision: 1.16 $
  + * $Date: 2002/03/15 22:51:08 $
    * ====================================================================
    *
    * The Apache Software License, Version 1.1
  @@ -89,7 +89,8 @@
    * @author dIon Gillard
    * @author Sean C. Sullivan
    * @author <a href="mailto:[EMAIL PROTECTED]";>John Evans</a>
  - * @version $Revision: 1.15 $ $Date: 2002/02/24 17:28:53 $
  + * @author Marc A. Saegesser
  + * @version $Revision: 1.16 $ $Date: 2002/03/15 22:51:08 $
    */
   
   public class Cookie extends NameValuePair implements Serializable, Comparator {
  @@ -336,6 +337,62 @@
   
   
       /**
  +     * Indicates whether the cookie had a path specified in a 
  +     * Path attribute in the set-cookie header.  This value
  +     * is important for generating the cookie header because 
  +     * RFC 2109 sec. 4.3.4 says that the cookie header should only 
  +     * include a $Path attribute if the cookie's path was specified
  +     * in the set-cookie header.
  +     *
  +     * @see #isPathAttributeSpecified
  +     * @param value True if the cookie's path came from a Path attribute.
  +     */
  +    public void setPathAttributeSpecified(boolean value)
  +    {
  +        _hasPathAttribute = value;
  +    }
  +
  +    /**
  +     * Returns true if cookie's path was set via a Path attribute in the
  +     * set-cookie header.
  +     *
  +     * @see #setPathAttributeSpecified
  +     * @return True if cookie's path was specified in the set-cookie header.
  +     */
  +    public boolean isPathAttributeSpecified()
  +    {
  +        return _hasPathAttribute;
  +    }
  +
  +    /**
  +     * Indicates whether the cookie had a domain specified in a 
  +     * Domain attribute in the set-cookie header.  This value
  +     * is important for generating the cookie header because 
  +     * RFC 2109 sec. 4.3.4 says that the cookie header should only 
  +     * include a $Domain attribute if the cookie's domain was specified
  +     * in the set-cookie header.
  +     *
  +     * @see #isDomainAttributeSpecified
  +     * @param value True if the cookie's domain came from a Domain attribute.
  +     */
  +    public void setDomainAttributeSpecified(boolean value)
  +    {
  +        _hasDomainAttribute = value;
  +    }
  +
  +    /**
  +     * Returns true if cookie's domain was set via a Domain attribute in the
  +     * set-cookie header.
  +     *
  +     * @see #setDomainAttributeSpecified
  +     * @return True if cookie's domain was specified in the set-cookie header.
  +     */
  +    public boolean isDomainAttributeSpecified()
  +    {
  +        return _hasDomainAttribute;
  +    }
  +
  +    /**
        * Returns a hash code in keeping with the
        * {@link Object#hashCode general hashCode contract}.
        */
  @@ -368,11 +425,11 @@
       public String toExternalForm() {
           StringBuffer buf = new StringBuffer();
           buf.append(getName()).append("=").append(getValue());
  -        if (_path != null) {
  +        if (_path != null && isPathAttributeSpecified()) {
               buf.append("; $Path=");
               buf.append(_path);
           }
  -        if (_domain != null) {
  +        if (_domain != null && isDomainAttributeSpecified()) {
               buf.append("; $Domain=");
               buf.append(_domain);
           }
  @@ -412,8 +469,8 @@
           //        should /foobar see the cookie? Probably not.
           return (
                   (getExpiryDate() == null || getExpiryDate().after(now)) && // only 
add the cookie if it hasn't yet expired
  -                (getDomain() == null || domain.endsWith(getDomain()) ) &&           
                 // and the domain pattern matches
  -                ((getPath() == null) || (path.startsWith(getPath()))) &&   // and 
the path is null or matching
  +                (getDomain() != null && domainMatch(domain, getDomain())) &&// and 
the domain pattern matches
  +                ((getPath() != null) && (path.startsWith(getPath()))) &&   // and 
the path is null or matching
                   (getSecure() ? secure : true)                              // and 
if the secure flag is set, only if the request is actually secure
                  );
       }
  @@ -449,9 +506,14 @@
        * <i>https</i> setting.
        * <p>
        * If no cookies match, returns null.
  +     * @exception java.lang.IllegalArgumentException if domain or path is null
        * @deprecated use the version which includes port number and date
        */
  -    public static Header createCookieHeader(String domain, String path, boolean 
secure, Cookie[] cookies) {
  +    public static Header createCookieHeader(String domain, String path, boolean 
secure, Cookie[] cookies) throws IllegalArgumentException {
  +        // Make sure domain isn't null here.  Path will be validated in subsequent 
call to createCookieHeader
  +        if(domain == null){
  +            throw new IllegalArgumentException("null domain in 
createCookieHeader.");
  +        }
           // parse port from domain, if any
           int port = secure ? 443 : 80;
           int ndx = domain.indexOf(":");
  @@ -459,7 +521,8 @@
               try {
                   port = Integer.parseInt(domain.substring(ndx+1,domain.length()));
               } catch(NumberFormatException e){
  -                // ignore?
  +                // ignore?, but at least log
  +                log.warn("Cookie.createCookieHeader():  Invalid port number in 
domain " + domain);
               }
           }
           return Cookie.createCookieHeader(domain,port,path,secure,cookies);
  @@ -472,8 +535,9 @@
        * <i>path</i> and <i>https</i> setting.
        * <p>
        * If no cookies match, returns null.
  +     * @exception java.lang.IllegalArgumentException if domain or path is null
        */
  -    public static Header createCookieHeader(String domain, int port, String path, 
boolean secure, Cookie[] cookies) {
  +    public static Header createCookieHeader(String domain, int port, String path, 
boolean secure, Cookie[] cookies) throws IllegalArgumentException {
           return Cookie.createCookieHeader(domain,port,path,secure,new 
Date(),cookies);
       }
   
  @@ -485,10 +549,20 @@
        * not expired according to the given <i>date</i>.
        * <p>
        * If no cookies match, returns null.
  +     * @exception java.lang.IllegalArgumentException if domain or path is null
        */
  -    public static Header createCookieHeader(String domain, int port, String path, 
boolean secure, Date now, Cookie[] cookies) {
  +    public static Header createCookieHeader(String domain, int port, String path, 
boolean secure, Date now, Cookie[] cookies) throws IllegalArgumentException {
  +        // Validate the domain and path
  +        if(domain == null){
  +            throw new IllegalArgumentException("null domain in 
createCookieHeader.");
  +        }
  +        if(path == null){
  +            throw new IllegalArgumentException("null path in createCookieHeader.");
  +        }
  +
           boolean added = false;
  -        StringBuffer value = new StringBuffer("$Version=1");
  +        StringBuffer value = new StringBuffer();
  +
           if (cookies.length <= 0) {
               return null;
           }
  @@ -500,6 +574,13 @@
               }
           }
           if (added) {
  +            /* FIXME.  We assume here that all the cookies we're sending back
  +             * are the same version.  A better solution might be to separate
  +             * cookies of different versions and send them on separate cookie
  +             * headers.
  +             */
  +            value.append("$Version=");
  +            value.append(((Cookie)addedCookies.get(0)).getVersion());
               for (Iterator itr = addedCookies.iterator(); itr.hasNext(); ) {
                   Cookie cookie = (Cookie)itr.next();
                   value.append("; ");
  @@ -570,8 +651,9 @@
        * @param setCookie the <tt>Set-Cookie</tt> {@link Header} received from the 
server
        * @return an array of <tt>Cookie</tt>s parsed from the Set-Cookie {@link 
Header}
        * @throws HttpException if an exception occurs during parsing
  +     * @throws java.lang.IllegalArgumentException if domain or path are null
        */
  -    public static Cookie[] parse(String domain, int port, String path, Header 
setCookie) throws HttpException {
  +    public static Cookie[] parse(String domain, int port, String path, Header 
setCookie) throws HttpException, IllegalArgumentException {
           return Cookie.parse(domain,port,path,false,setCookie);
       }
   
  @@ -585,8 +667,9 @@
        * @param setCookie the <tt>Set-Cookie</tt> {@link Header} received from the 
server
        * @return an array of <tt>Cookie</tt>s parsed from the Set-Cookie {@link 
Header}
        * @throws HttpException if an exception occurs during parsing
  +     * @throws java.lang.IllegalArgumentException if domain or path are null
        */
  -    public static Cookie[] parse(String domain, String path, Header setCookie) 
throws HttpException {
  +    public static Cookie[] parse(String domain, String path, Header setCookie) 
throws HttpException, IllegalArgumentException {
           return Cookie.parse(domain,80,path,false,setCookie);
       }
   
  @@ -600,6 +683,7 @@
        * @param setCookie the <tt>Set-Cookie</tt> {@link Header} received from the 
server
        * @return an array of <tt>Cookie</tt>s parsed from the Set-Cookie {@link 
Header}
        * @throws HttpException if an exception occurs during parsing
  +     * @throws java.lang.IllegalArgumentException if domain or path are null
        */
       public static Cookie[] parse(String domain, String path, boolean secure, Header 
setCookie) throws HttpException {
           return Cookie.parse(domain,(secure ? 443 : 80),path,secure,setCookie);
  @@ -631,8 +715,33 @@
         * @param setCookie the <tt>Set-Cookie</tt> {@link Header} received from the 
server
         * @return an array of <tt>Cookie</tt>s parsed from the Set-Cookie {@link 
Header}
         * @throws HttpException if an exception occurs during parsing
  +      * @throws java.lang.IllegalArgumentException if domain or path are null
         */
  -    public static Cookie[] parse(String domain, int port, String path, boolean 
secure, Header setCookie) throws HttpException {
  +    public static Cookie[] parse(String domain, int port, String path, boolean 
secure, Header setCookie) throws HttpException, IllegalArgumentException {
  +        // Validate domain and path
  +        if(domain == null){
  +            throw new IllegalArgumentException("domain may not be null.");
  +        }
  +        if(path == null){
  +            throw new IllegalArgumentException("path may not be null.");
  +        }
  +
  +        /* Build the default path.  Per RFC 2109/4.3.1 this is the 
  +         * request path up to, but not including, the right-most / charater.
  +         */
  +        if(path.length() == 0){
  +            log.debug("Cookie.parse():  Fixing up empty request path.");
  +            path = "/";
  +        }
  +        String defaultPath = null;
  +        int lastSlashIndex = path.lastIndexOf("/");
  +        if(lastSlashIndex == 0){
  +            defaultPath = "/";
  +        }else if(lastSlashIndex > 0){
  +            defaultPath = path.substring(0, lastSlashIndex);
  +        }else{
  +            defaultPath = path;
  +        }
           HeaderElement[] headerElements =
               HeaderElement.parse(setCookie.getValue());
   
  @@ -642,7 +751,10 @@
   
               Cookie cookie = new Cookie(domain,
                                          headerElements[i].getName(),
  -                                       headerElements[i].getValue());
  +                                       headerElements[i].getValue(),
  +                                       defaultPath, 
  +                                       null,
  +                                       false);
   
               // cycle through the parameters
               NameValuePair[] parameters = headerElements[i].getParameters();
  @@ -683,6 +795,7 @@
                           }
                       } else if (name.equals("path")) {
                           cookie.setPath(parameters[j].getValue());
  +                        cookie.setPathAttributeSpecified(true);
                       } else if (name.equals("domain")) {
                           String d = parameters[j].getValue().toLowerCase();
                           // add leading dot if not present and if domain is
  @@ -700,6 +813,7 @@
                               cookie.setDomain("." + d);
                           else
                               cookie.setDomain(d);
  +                        cookie.setDomainAttributeSpecified(true);
                       } else if (name.equals("max-age")) {
                           int age;
                           try {
  @@ -757,8 +871,11 @@
               // security check... we musn't allow the server to give us an
               // invalid domain scope
   
  -            // domain must be either .local or must contain at least two dots
  -            if (cookie.getDomain() != null && 
!cookie.getDomain().equals("localhost")) {
  +            // Validate the cookies domain attribute.  NOTE:  Domains without any 
dots are
  +            // allowed to support hosts on private LANs that don't have DNS names.  
Since
  +            // they have no dots, to domain-match the request-host and domain must 
be identical
  +            // for the cookie to sent back to the origin-server.
  +            if (cookie.getDomain() != null && 
!cookie.getDomain().equals("localhost") && domain.indexOf(".") >= 0) {
   
                   // Not required to have at least two dots.  RFC 2965.
                   // A Set-Cookie2 with Domain=ajax.com will be accepted.
  @@ -775,8 +892,8 @@
   
                   if(cookie.getVersion() == 0){
                       // Validate domain using Netscape cookie specification
  -                    int domainParts = new StringTokenizer(domain, 
".").countTokens();
  -                    if(isSpecialDomain(domain)){
  +                    int domainParts = new StringTokenizer(cookie.getDomain(), 
".").countTokens();
  +                    if(isSpecialDomain(cookie.getDomain())){
                           if(domainParts < 2){
                               if(log.isInfoEnabled()) {
                                   log.info("Cookie.parse(): Rejecting set cookie 
header \"" + setCookie.getValue() + "\" because \"" + cookie.getName() + "\" has an 
illegal domain attribute (\"" + cookie.getDomain() + "\") for the given domain \"" + 
domain + "\".  It violoates the Netscape cookie specification for special TLDs.");
  @@ -797,6 +914,13 @@
   
                       }
                   }else{
  +                    // domain must have at least one embedded dot
  +                    int dotIndex = cookie.getDomain().indexOf('.', 1);
  +                    if(dotIndex < 0 || dotIndex == cookie.getDomain().length()-1){
  +                        throw new HttpException("Bad set-cookie header:  " + 
setCookie.getValue() + 
  +                                                "Illegal domain attribute " + 
cookie.getDomain() +
  +                                                ".  The domain contains no embedded 
dots.");
  +                    }
                       // host minus domain may not contain any dots
                       if (domain.substring(0,
                               domain.length() -
  @@ -851,6 +975,10 @@
                       }
                   }
               }
  +
  +            if(log.isDebugEnabled()){
  +                log.debug("Cookie.parse():  Adding cookie - " + cookie.toString());
  +            }
               cookies[index++] = cookie;
           }
   
  @@ -879,7 +1007,18 @@
       }
   
       /**
  -     * Adds the given cookie into the given in descending path order.  That is, 
  +     * Performs a domain-match as described in RFC2109.
  +     */
  +    private static boolean domainMatch(String host, String domain)
  +    {
  +        boolean match = host.equals(domain) ||
  +                        (domain.startsWith(".") && host.endsWith(domain));
  +
  +        return match;
  +    }
  +
  +    /**
  +     * Adds the given cookie into the given list in descending path order.  That 
is, 
        * more specific path to least specific paths.  This may not be the fastest
        * algorythm, but it'll work OK for the small number of cookies we're 
        * generally dealing with.
  @@ -916,6 +1055,12 @@
   
      /** My secure flag. */
      private boolean _secure;
  +
  +   /** Specifies if the set-cookie header included a Path attribute for this cookie 
*/
  +   private boolean _hasPathAttribute = false;
  +
  +   /** Specifies if the set-cookie header included a Domain attribute for this 
cookie */
  +   private boolean _hasDomainAttribute = false;
   
      /** The version of the cookie specification I was created from. */
      private int     _version = 0;
  
  
  

--
To unsubscribe, e-mail:   <mailto:[EMAIL PROTECTED]>
For additional commands, e-mail: <mailto:[EMAIL PROTECTED]>

Reply via email to