[mod_python] Cookie patch

Nicolas Lehuen nicolas.lehuen at gmail.com
Thu Jan 13 12:50:20 EST 2005


On Thu, 13 Jan 2005 18:48:08 +0100, Nicolas Lehuen
<nicolas.lehuen at gmail.com> wrote:
> On Thu, 13 Jan 2005 09:23:03 -0700, Craig Warren
> <craig.warren at encorp.com> wrote:
> >  I found an error while with Cookie module.  When the cookie module parses a
> > cookie, if that cooke has $Version or $Path in it you get an error. My
> > cookie is coming from a java libaray, that puts $Version and $Path in it.
> >  example ="Cookie: $Version=0; pysid=34a9b38c34;$Path=/"
> >
> >
> >  RFC 2109 mentions $Version and $Path in Section 4.4
> >  http://www.faqs.org/rfcs/rfc2109.html 4.4 How an Origin Server Interprets
> > the Cookie Header A user agent returns much of the information in the
> > Set-Cookie header to the origin server when the Path attribute matches that
> > of a new request. When it receives a Cookie header, the origin server should
> > treat cookies with NAMEs whose prefix is $ specially, as an attribute for
> > the adjacent cookie. The value for such a NAME is to be interpreted as
> > applying to the lexically (left-to-right) most recent cookie whose name does
> > not have the $ prefix. If there is no previous cookie, the value applies to
> > the cookie mechanism as a whole. For example, consider the cookie Cookie:
> > $Version="1"; Customer="WILE_E_COYOTE"; $Path="/acme" $Version applies to
> > the cookie mechanism as a whole (and gives the version number for the cookie
> > mechanism). $Path is an attribute whose value (/acme) defines the Path
> > attribute that was used when the Customer cookie was defined in a Set-Cookie
> > response header. In Cookie.py it looks like the code was in place to deal
> > with $Version and $Path, but not finished
> >
> >  from  _parse_cookie()
> >  line ~321
> >   l_key = key.lower()
> >
> >          if (l_key in valid or key[0] == '$'):
> >
> >              # "internal" attribute, add to cookie
> >
> >              if l_key == "max-age":
> >                  l_key = "max_age"
> >              setattr(c, l_key, val)
> >
> >   The above code checks for the $, but doesn't do anything with it and in
> > fact when it tries to do a setattr with $Version or $Path, you get an error.
> >
> >  I modified the function to be
> >
> >  l_key = key.lower()
> >
> >          if (l_key in valid or key[0] == '$'):
> >
> >              # "internal" attribute, add to cookie
> >
> >              if l_key == "max-age":
> >                  l_key = "max_age"
> >              if key[0] == '$':
> >                  l_key = l_key[1:]
> >              setattr(c, l_key, val)
> >
> >
> >  Don't know if this is exactly the correct fix, but it works for me and I
> > thought that I would email the list.  I tried to subscribe to
> > python-dev at httpd.apache.org, but haven't gotten a response back yet, I CC
> > this message to python-dev at httpd.apache.org also.
> >
> >  Craig Warren
> 
> Hi,
> 
> Yeah, your fix seems correct.
> 
> The Cookie class has a metaclass which defines slots, so you cannot
> have arbitrary attribute names. The only valid attributes names are
> defined on line 58 ( _valid_attr =...) of Cookie.py, and the version
> and path attributes do not begin with a $. So the attribute names
> which are parsed in _parse_cookie() must have their initial $ removed.
> 
> The problem of the current code is that the _valid_attr check is
> useless, but it's not totally spec compliant since the valid attribute
> coming first (as the $Version attribute) should be applied to all
> cookies. So I rewrote the whole function like this :
> 
> def _parse_cookie(str, Class):
>     # XXX problem is we should allow duplicate
>     # strings
>     result = {}
> 
>     all_cookies_attribute = {}
> 
>     valid = Cookie._valid_attr
> 
>     c = None
>     matchIter = _cookiePattern.finditer(str)
> 
>     for match in matchIter:
> 
>         key, val = match.group("key"), match.group("val")
> 
>         # we will check whether the cookie name is a valid attribute name
>         # for the previous cookie.
>         l_key = key.lower()
>         # fix from Craig Warren
>         if l_key[0]=='$':
>             l_key=l_key[1:]
>         if l_key == "max-age":
>             l_key = "max_age"
>         
>         if l_key in valid:
>             if not c:
>                 # 'global' attribute, will be added to all cookies
>                 all_cookies_attribute[l_key]=val
>             else:
>                 # "internal" attribute, add to cookie
>                 setattr(c, l_key, val)
>         else:
>             # start a new cookie
>             # we don't use l_key so that we keep the initial name
>             # this way we are consistent with the creation of the first cookie
>             # as done in the previous version of the function
>             c = Class(key, val)
> 
>             # XXX this is a bit heavyweight since usually we'll have only 0 or 1
>             # global attribute...
>             for key, val in all_cookies_attribute.items():
>                 setattr(c,key,val)
> 
>             result[key] = c
> 
>     return result
> 
> Since you've already set up a testing environment, can you test this
> new version of the parsing function ? If it works, I'll check it in.
> 
> Regards,
> Nicolas
> 

Woops sorry, I found a bug *before* the test... The iteration on
all_cookies_attribute at the end of the method will overwrite the key
value. So the new version is really :

def _parse_cookie(str, Class):
    # XXX problem is we should allow duplicate
    # strings
    result = {}

    all_cookies_attribute = {}

    valid = Cookie._valid_attr

    c = None
    matchIter = _cookiePattern.finditer(str)

    for match in matchIter:

        key, val = match.group("key"), match.group("val")

        # we will check whether the cookie name is a valid attribute name
        # for the previous cookie.
        l_key = key.lower()
        # fix from Craig Warren
        if l_key[0]=='$':
            l_key=l_key[1:]
        if l_key == "max-age":
            l_key = "max_age"
        
        if l_key in valid:
            if not c:
                # 'global' attribute, will be added to all cookies
                all_cookies_attribute[l_key]=val
            else:
                # "internal" attribute, add to cookie
                setattr(c, l_key, val)
        else:
            # start a new cookie
            # we don't use l_key so that we keep the initial name
            # this way we are consistent with the creation of the first cookie
            # as done in the previous version of the function
            c = Class(key, val)
            result[key] = c

            # XXX this is a bit heavyweight since usually we'll have only 0 or 1
            # global attribute...
            for key, val in all_cookies_attribute.items():
                setattr(c,key,val)

    return result


More information about the Mod_python mailing list