Commit 532efabf authored by Georg Brandl's avatar Georg Brandl

patch #848017: make Cookie more RFC-compliant.

parent e1b13d20
...@@ -98,7 +98,9 @@ In general, it should be the case that \method{value_encode()} and ...@@ -98,7 +98,9 @@ In general, it should be the case that \method{value_encode()} and
Return a string representation suitable to be sent as HTTP headers. Return a string representation suitable to be sent as HTTP headers.
\var{attrs} and \var{header} are sent to each \class{Morsel}'s \var{attrs} and \var{header} are sent to each \class{Morsel}'s
\method{output()} method. \var{sep} is used to join the headers \method{output()} method. \var{sep} is used to join the headers
together, and is by default a newline. together, and is by default the combination '\r\n' (CRLF).
\versionchanged[The default separator has been changed from '\n' to match the cookie
specification]{2.5}
\end{methoddesc} \end{methoddesc}
\begin{methoddesc}[BaseCookie]{js_output}{\optional{attrs}} \begin{methoddesc}[BaseCookie]{js_output}{\optional{attrs}}
...@@ -195,32 +197,32 @@ The following example demonstrates how to use the \module{Cookie} module. ...@@ -195,32 +197,32 @@ The following example demonstrates how to use the \module{Cookie} module.
>>> C["fig"] = "newton" >>> C["fig"] = "newton"
>>> C["sugar"] = "wafer" >>> C["sugar"] = "wafer"
>>> print C # generate HTTP headers >>> print C # generate HTTP headers
Set-Cookie: sugar=wafer; Set-Cookie: sugar=wafer
Set-Cookie: fig=newton; Set-Cookie: fig=newton
>>> print C.output() # same thing >>> print C.output() # same thing
Set-Cookie: sugar=wafer; Set-Cookie: sugar=wafer
Set-Cookie: fig=newton; Set-Cookie: fig=newton
>>> C = Cookie.SmartCookie() >>> C = Cookie.SmartCookie()
>>> C["rocky"] = "road" >>> C["rocky"] = "road"
>>> C["rocky"]["path"] = "/cookie" >>> C["rocky"]["path"] = "/cookie"
>>> print C.output(header="Cookie:") >>> print C.output(header="Cookie:")
Cookie: rocky=road; Path=/cookie; Cookie: rocky=road; Path=/cookie
>>> print C.output(attrs=[], header="Cookie:") >>> print C.output(attrs=[], header="Cookie:")
Cookie: rocky=road; Cookie: rocky=road
>>> C = Cookie.SmartCookie() >>> C = Cookie.SmartCookie()
>>> C.load("chips=ahoy; vienna=finger") # load from a string (HTTP header) >>> C.load("chips=ahoy; vienna=finger") # load from a string (HTTP header)
>>> print C >>> print C
Set-Cookie: vienna=finger; Set-Cookie: vienna=finger
Set-Cookie: chips=ahoy; Set-Cookie: chips=ahoy
>>> C = Cookie.SmartCookie() >>> C = Cookie.SmartCookie()
>>> C.load('keebler="E=everybody; L=\\"Loves\\"; fudge=\\012;";') >>> C.load('keebler="E=everybody; L=\\"Loves\\"; fudge=\\012;";')
>>> print C >>> print C
Set-Cookie: keebler="E=everybody; L=\"Loves\"; fudge=\012;"; Set-Cookie: keebler="E=everybody; L=\"Loves\"; fudge=\012;"
>>> C = Cookie.SmartCookie() >>> C = Cookie.SmartCookie()
>>> C["oreo"] = "doublestuff" >>> C["oreo"] = "doublestuff"
>>> C["oreo"]["path"] = "/" >>> C["oreo"]["path"] = "/"
>>> print C >>> print C
Set-Cookie: oreo=doublestuff; Path=/; Set-Cookie: oreo=doublestuff; Path=/
>>> C = Cookie.SmartCookie() >>> C = Cookie.SmartCookie()
>>> C["twix"] = "none for you" >>> C["twix"] = "none for you"
>>> C["twix"].value >>> C["twix"].value
...@@ -233,8 +235,8 @@ Set-Cookie: oreo=doublestuff; Path=/; ...@@ -233,8 +235,8 @@ Set-Cookie: oreo=doublestuff; Path=/;
>>> C["string"].value >>> C["string"].value
'seven' 'seven'
>>> print C >>> print C
Set-Cookie: number=7; Set-Cookie: number=7
Set-Cookie: string=seven; Set-Cookie: string=seven
>>> C = Cookie.SerialCookie() >>> C = Cookie.SerialCookie()
>>> C["number"] = 7 >>> C["number"] = 7
>>> C["string"] = "seven" >>> C["string"] = "seven"
...@@ -243,8 +245,8 @@ Set-Cookie: string=seven; ...@@ -243,8 +245,8 @@ Set-Cookie: string=seven;
>>> C["string"].value >>> C["string"].value
'seven' 'seven'
>>> print C >>> print C
Set-Cookie: number="I7\012."; Set-Cookie: number="I7\012."
Set-Cookie: string="S'seven'\012p1\012."; Set-Cookie: string="S'seven'\012p1\012."
>>> C = Cookie.SmartCookie() >>> C = Cookie.SmartCookie()
>>> C["number"] = 7 >>> C["number"] = 7
>>> C["string"] = "seven" >>> C["string"] = "seven"
...@@ -253,6 +255,6 @@ Set-Cookie: string="S'seven'\012p1\012."; ...@@ -253,6 +255,6 @@ Set-Cookie: string="S'seven'\012p1\012.";
>>> C["string"].value >>> C["string"].value
'seven' 'seven'
>>> print C >>> print C
Set-Cookie: number="I7\012."; Set-Cookie: number="I7\012."
Set-Cookie: string=seven; Set-Cookie: string=seven
\end{verbatim} \end{verbatim}
...@@ -69,9 +69,8 @@ a dictionary. ...@@ -69,9 +69,8 @@ a dictionary.
>>> C = Cookie.SmartCookie() >>> C = Cookie.SmartCookie()
>>> C["fig"] = "newton" >>> C["fig"] = "newton"
>>> C["sugar"] = "wafer" >>> C["sugar"] = "wafer"
>>> print C >>> C.output()
Set-Cookie: fig=newton; 'Set-Cookie: fig=newton\r\nSet-Cookie: sugar=wafer'
Set-Cookie: sugar=wafer;
Notice that the printable representation of a Cookie is the Notice that the printable representation of a Cookie is the
appropriate format for a Set-Cookie: header. This is the appropriate format for a Set-Cookie: header. This is the
...@@ -82,9 +81,9 @@ attributes by using the .output() function ...@@ -82,9 +81,9 @@ attributes by using the .output() function
>>> C["rocky"] = "road" >>> C["rocky"] = "road"
>>> C["rocky"]["path"] = "/cookie" >>> C["rocky"]["path"] = "/cookie"
>>> print C.output(header="Cookie:") >>> print C.output(header="Cookie:")
Cookie: rocky=road; Path=/cookie; Cookie: rocky=road; Path=/cookie
>>> print C.output(attrs=[], header="Cookie:") >>> print C.output(attrs=[], header="Cookie:")
Cookie: rocky=road; Cookie: rocky=road
The load() method of a Cookie extracts cookies from a string. In a The load() method of a Cookie extracts cookies from a string. In a
CGI script, you would use this method to extract the cookies from the CGI script, you would use this method to extract the cookies from the
...@@ -92,9 +91,8 @@ HTTP_COOKIE environment variable. ...@@ -92,9 +91,8 @@ HTTP_COOKIE environment variable.
>>> C = Cookie.SmartCookie() >>> C = Cookie.SmartCookie()
>>> C.load("chips=ahoy; vienna=finger") >>> C.load("chips=ahoy; vienna=finger")
>>> print C >>> C.output()
Set-Cookie: chips=ahoy; 'Set-Cookie: chips=ahoy\r\nSet-Cookie: vienna=finger'
Set-Cookie: vienna=finger;
The load() method is darn-tootin smart about identifying cookies The load() method is darn-tootin smart about identifying cookies
within a string. Escaped quotation marks, nested semicolons, and other within a string. Escaped quotation marks, nested semicolons, and other
...@@ -103,7 +101,7 @@ such trickeries do not confuse it. ...@@ -103,7 +101,7 @@ such trickeries do not confuse it.
>>> C = Cookie.SmartCookie() >>> C = Cookie.SmartCookie()
>>> C.load('keebler="E=everybody; L=\\"Loves\\"; fudge=\\012;";') >>> C.load('keebler="E=everybody; L=\\"Loves\\"; fudge=\\012;";')
>>> print C >>> print C
Set-Cookie: keebler="E=everybody; L=\"Loves\"; fudge=\012;"; Set-Cookie: keebler="E=everybody; L=\"Loves\"; fudge=\012;"
Each element of the Cookie also supports all of the RFC 2109 Each element of the Cookie also supports all of the RFC 2109
Cookie attributes. Here's an example which sets the Path Cookie attributes. Here's an example which sets the Path
...@@ -113,7 +111,7 @@ attribute. ...@@ -113,7 +111,7 @@ attribute.
>>> C["oreo"] = "doublestuff" >>> C["oreo"] = "doublestuff"
>>> C["oreo"]["path"] = "/" >>> C["oreo"]["path"] = "/"
>>> print C >>> print C
Set-Cookie: oreo=doublestuff; Path=/; Set-Cookie: oreo=doublestuff; Path=/
Each dictionary element has a 'value' attribute, which gives you Each dictionary element has a 'value' attribute, which gives you
back the value associated with the key. back the value associated with the key.
...@@ -144,9 +142,8 @@ the value to a string, when the values are set dictionary-style. ...@@ -144,9 +142,8 @@ the value to a string, when the values are set dictionary-style.
'7' '7'
>>> C["string"].value >>> C["string"].value
'seven' 'seven'
>>> print C >>> C.output()
Set-Cookie: number=7; 'Set-Cookie: number=7\r\nSet-Cookie: string=seven'
Set-Cookie: string=seven;
SerialCookie SerialCookie
...@@ -165,9 +162,8 @@ values, however.) ...@@ -165,9 +162,8 @@ values, however.)
7 7
>>> C["string"].value >>> C["string"].value
'seven' 'seven'
>>> print C >>> C.output()
Set-Cookie: number="I7\012."; 'Set-Cookie: number="I7\\012."\r\nSet-Cookie: string="S\'seven\'\\012p1\\012."'
Set-Cookie: string="S'seven'\012p1\012.";
Be warned, however, if SerialCookie cannot de-serialize a value (because Be warned, however, if SerialCookie cannot de-serialize a value (because
it isn't a valid pickle'd object), IT WILL RAISE AN EXCEPTION. it isn't a valid pickle'd object), IT WILL RAISE AN EXCEPTION.
...@@ -190,9 +186,8 @@ as a string. ...@@ -190,9 +186,8 @@ as a string.
7 7
>>> C["string"].value >>> C["string"].value
'seven' 'seven'
>>> print C >>> C.output()
Set-Cookie: number="I7\012."; 'Set-Cookie: number="I7\\012."\r\nSet-Cookie: string=seven'
Set-Cookie: string=seven;
Backwards Compatibility Backwards Compatibility
...@@ -228,7 +223,7 @@ __all__ = ["CookieError","BaseCookie","SimpleCookie","SerialCookie", ...@@ -228,7 +223,7 @@ __all__ = ["CookieError","BaseCookie","SimpleCookie","SerialCookie",
"SmartCookie","Cookie"] "SmartCookie","Cookie"]
_nulljoin = ''.join _nulljoin = ''.join
_spacejoin = ' '.join _semispacejoin = '; '.join
# #
# Define an exception visible to External modules # Define an exception visible to External modules
...@@ -485,7 +480,7 @@ class Morsel(dict): ...@@ -485,7 +480,7 @@ class Morsel(dict):
RA = result.append RA = result.append
# First, the key=value pair # First, the key=value pair
RA("%s=%s;" % (self.key, self.coded_value)) RA("%s=%s" % (self.key, self.coded_value))
# Now add any defined attributes # Now add any defined attributes
if attrs is None: if attrs is None:
...@@ -496,16 +491,16 @@ class Morsel(dict): ...@@ -496,16 +491,16 @@ class Morsel(dict):
if V == "": continue if V == "": continue
if K not in attrs: continue if K not in attrs: continue
if K == "expires" and type(V) == type(1): if K == "expires" and type(V) == type(1):
RA("%s=%s;" % (self._reserved[K], _getdate(V))) RA("%s=%s" % (self._reserved[K], _getdate(V)))
elif K == "max-age" and type(V) == type(1): elif K == "max-age" and type(V) == type(1):
RA("%s=%d;" % (self._reserved[K], V)) RA("%s=%d" % (self._reserved[K], V))
elif K == "secure": elif K == "secure":
RA("%s;" % self._reserved[K]) RA(str(self._reserved[K]))
else: else:
RA("%s=%s;" % (self._reserved[K], V)) RA("%s=%s" % (self._reserved[K], V))
# Return the result # Return the result
return _spacejoin(result) return _semispacejoin(result)
# end OutputString # end OutputString
# end Morsel class # end Morsel class
...@@ -581,7 +576,7 @@ class BaseCookie(dict): ...@@ -581,7 +576,7 @@ class BaseCookie(dict):
self.__set(key, rval, cval) self.__set(key, rval, cval)
# end __setitem__ # end __setitem__
def output(self, attrs=None, header="Set-Cookie:", sep="\n"): def output(self, attrs=None, header="Set-Cookie:", sep="\015\012"):
"""Return a string suitable for HTTP.""" """Return a string suitable for HTTP."""
result = [] result = []
items = self.items() items = self.items()
...@@ -599,7 +594,7 @@ class BaseCookie(dict): ...@@ -599,7 +594,7 @@ class BaseCookie(dict):
items.sort() items.sort()
for K,V in items: for K,V in items:
L.append( '%s=%s' % (K,repr(V.value) ) ) L.append( '%s=%s' % (K,repr(V.value) ) )
return '<%s: %s>' % (self.__class__.__name__, _spacejoin(L)) return '<%s: %s>' % (self.__class__.__name__, _semispacejoin(L))
def js_output(self, attrs=None): def js_output(self, attrs=None):
"""Return a string suitable for JavaScript.""" """Return a string suitable for JavaScript."""
......
...@@ -193,6 +193,9 @@ Extension Modules ...@@ -193,6 +193,9 @@ Extension Modules
Library Library
------- -------
- Patch #848017: Make Cookie more RFC-compliant. Use CRLF as default output
separator and do not output trailing semicola.
- Patch #1062060: urllib.urlretrieve() now raises a new exception, named - Patch #1062060: urllib.urlretrieve() now raises a new exception, named
ContentTooShortException, when the actually downloaded size does not ContentTooShortException, when the actually downloaded size does not
match the Content-Length header. match the Content-Length header.
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment