[Zope3-checkins] SVN: Zope3/trunk/src/zope/publisher/ Add tests and refactor cookie handling to use standard library Cookie.py

Stuart Bishop stuart at stuartbishop.net
Wed Jul 14 23:08:12 EDT 2004


Log message for revision 26545:
  Add tests and refactor cookie handling to use standard library Cookie.py 
  so that they pass
  


Changed:
  U   Zope3/trunk/src/zope/publisher/http.py
  U   Zope3/trunk/src/zope/publisher/tests/test_http.py


-=-
Modified: Zope3/trunk/src/zope/publisher/http.py
===================================================================
--- Zope3/trunk/src/zope/publisher/http.py	2004-07-15 02:45:08 UTC (rev 26544)
+++ Zope3/trunk/src/zope/publisher/http.py	2004-07-15 03:08:12 UTC (rev 26545)
@@ -19,6 +19,7 @@
 from urllib import quote, unquote, splitport
 from types import StringTypes, ClassType
 from cgi import escape
+from Cookie import SimpleCookie
 
 from zope.interface import implements
 
@@ -324,37 +325,16 @@
 
         return '%s://%s' % (protocol, host)
 
-    _cookieFormat = re.compile('[\x00- ]*'
-            # Cookie name
-            '([^\x00- ;,="]+)='
-            # Cookie value (either correct quoted or MSIE)
-            '(?:"([^"]*)"|([^\x00- ;,"]*))'
-            '(?:[\x00- ]*[;,])?[\x00- ]*')
-
     def _parseCookies(self, text, result=None):
         """Parse 'text' and return found cookies as 'result' dictionary."""
 
         if result is None:
             result = {}
 
-        cookieFormat = self._cookieFormat
+        c = SimpleCookie(text)
+        for k,v in c.items():
+            result[unicode(k, ENCODING)] = unicode(v.value, ENCODING)
 
-        pos = 0
-        ln = len(text)
-        while pos < ln:
-            match = cookieFormat.match(text, pos)
-            if match is None:
-                break
-
-            name  = unicode(match.group(1), ENCODING)
-            if name not in result:
-                value, ms_value = match.group(2, 3)
-                if value is None:
-                    value = ms_value
-                result[name] = unicode(value, ENCODING)
-
-            pos = match.end()
-
         return result
 
     def __setupCookies(self):
@@ -720,7 +700,7 @@
 
         for k, v in kw.items():
             if v is not None:
-                cookie[k] = v
+                cookie[k.lower()] = v
 
         cookie['value'] = value
 
@@ -829,36 +809,25 @@
         return location
 
     def _cookie_list(self):
-        cookie_list = []
+        c = SimpleCookie()
         for name, attrs in self._cookies.items():
+            name = str(name)
+            c[name] = attrs['value'].encode(ENCODING)
+            for k,v in attrs.items():
+                if k == 'value':
+                    continue
+                if k == 'secure':
+                    if v:
+                        c[name]['secure'] = True
+                    continue
+                if k == 'max_age':
+                    k = 'max-age'
+                elif k == 'comment':
+                    # Encode rather than throw an exception
+                    v = quote(v.encode('utf-8'), safe="/?:@&+")
+                c[name][k] = str(v)
+        return str(c).splitlines()
 
-            # Note that as of May 98, IE4 ignores cookies with
-            # quoted cookie attr values, so only the value part
-            # of name=value pairs may be quoted.
-
-            cookie='Set-Cookie: %s="%s"' % (name, attrs['value'])
-            for name, value in attrs.items():
-                name = name.lower()
-                if name == 'expires':
-                    cookie = '%s; Expires=%s' % (cookie,value)
-                elif name == 'domain':
-                    cookie = '%s; Domain=%s' % (cookie,value)
-                elif name == 'path':
-                    cookie = '%s; Path=%s' % (cookie,value)
-                elif name == 'max_age':
-                    cookie = '%s; Max-Age=%s' % (cookie,value)
-                elif name == 'comment':
-                    cookie = '%s; Comment=%s' % (cookie,value)
-                elif name == 'secure' and value:
-                    cookie = '%s; Secure' % cookie
-            cookie_list.append(cookie)
-
-        # TODO: Should really check size of cookies here!
-        # Why?
-
-        return cookie_list
-
-
     def getHeaderText(self, m):
         lst = ['Status: %s %s' % (self._status, self._reason)]
         items = m.items()

Modified: Zope3/trunk/src/zope/publisher/tests/test_http.py
===================================================================
--- Zope3/trunk/src/zope/publisher/tests/test_http.py	2004-07-15 02:45:08 UTC (rev 26544)
+++ Zope3/trunk/src/zope/publisher/tests/test_http.py	2004-07-15 03:08:12 UTC (rev 26545)
@@ -32,6 +32,7 @@
 from zope.interface.verify import verifyObject
 
 from StringIO import StringIO
+from Cookie import SimpleCookie, CookieError
 
 
 class UserStub:
@@ -212,7 +213,8 @@
 
     def testCookies(self):
         cookies = {
-            'HTTP_COOKIE': 'foo=bar; spam="eggs", this="Should be accepted"'
+            'HTTP_COOKIE':
+                'foo=bar; path=/; spam="eggs", this="Should be accepted"'
         }
         req = self._createRequest(extra_env=cookies)
 
@@ -225,6 +227,15 @@
         self.assertEquals(req.cookies[u'this'], u'Should be accepted')
         self.assertEquals(req[u'this'], u'Should be accepted')
 
+        # Reserved key
+        self.failIf(req.cookies.has_key('path'))
+
+    def testCookiesUnicode(self):
+        # Cookie values are assumed to be UTF-8 encoded
+        cookies = {'HTTP_COOKIE': r'key="\342\230\243";'}
+        req = self._createRequest(extra_env=cookies)
+        self.assertEquals(req.cookies[u'key'], u'\N{BIOHAZARD SIGN}')
+
     def testHeaders(self):
         headers = {
             'TEST_HEADER': 'test',
@@ -421,7 +432,15 @@
         headers = {}
         for line in hdrs_text.splitlines():
             key, val = line.split(":", 1)
-            headers[key.strip()] = val.strip()
+            key = key.strip()
+            val = val.strip()
+            if headers.has_key(key):
+                if type(headers[key]) == type([]):
+                    headers[key].append(val)
+                else:
+                    headers[key] = [headers[key], val]
+            else:
+                headers[key] = val
         return headers, body
 
     def _getResultFromResponse(self, body, charset=None, headers=None):
@@ -476,7 +495,68 @@
         eq("image/gif", headers["Content-Type"])
         eq("test", body)
 
+    def _getCookieFromResponse(self, cookies):
+        # Shove the cookies through request, parse the Set-Cookie header
+        # and spit out a list of headers for examination
+        response, stream = self._createResponse()
+        for name, value, kw in cookies:
+            response.setCookie(name, value, **kw)
+        response.setBody('test')
+        response.outputBody()
+        headers, body = self._parseResult(stream.getvalue())
+        c = SimpleCookie()
+        cookie_headers = headers["Set-Cookie"]
+        if type(cookie_headers) != type([]):
+            cookie_headers = [cookie_headers]
+        return cookie_headers
 
+    def testSetCookie(self):
+        c = self._getCookieFromResponse([
+                ('foo', 'bar', {}),
+                ])
+        self.failUnless('foo=bar;' in c, 'foo=bar not in %r' % c)
+
+        c = self._getCookieFromResponse([
+                ('foo', 'bar', {}),
+                ('alpha', 'beta', {}),
+                ])
+        self.failUnless('foo=bar;' in c)
+        self.failUnless('alpha=beta;' in c)
+
+        c = self._getCookieFromResponse([
+                ('sign', u'\N{BIOHAZARD SIGN}', {}),
+                ])
+        self.failUnless(r'sign="\342\230\243";' in c)
+
+        self.assertRaises(
+                CookieError,
+                self._getCookieFromResponse,
+                [('path', 'invalid key', {}),]
+                )
+
+        c = self._getCookieFromResponse([
+                ('foo', 'bar', {
+                    'Expires': 'Sat, 12 Jul 2014 23:26:28 GMT',
+                    'domain': 'example.com',
+                    'pAth': '/froboz',
+                    'max_age': 3600,
+                    'comment': u'blah;\N{BIOHAZARD SIGN}?',
+                    'seCure': True,
+                    }),
+                ])[0]
+        self.failUnless('foo=bar;' in c)
+        self.failUnless('expires=Sat, 12 Jul 2014 23:26:28 GMT;' in c, repr(c))
+        self.failUnless('Domain=example.com;' in c)
+        self.failUnless('Path=/froboz;' in c)
+        self.failUnless('Max-Age=3600;' in c)
+        self.failUnless('Comment=blah%3B%E2%98%A3?;' in c, repr(c))
+        self.failUnless('secure;' in c)
+
+        c = self._getCookieFromResponse([('foo', 'bar', {'secure': False})])[0]
+        self.failUnless('foo=bar;' in c)
+        self.failIf('secure' in c)
+
+
 def test_suite():
     suite = unittest.TestSuite()
     suite.addTest(unittest.makeSuite(ConcreteHTTPTests))



More information about the Zope3-Checkins mailing list