From: Ben Darnell Date: Sun, 10 Jul 2011 22:30:52 +0000 (-0700) Subject: Properly quote and unquote cookie values. X-Git-Tag: v2.1.0~95 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=f771360a84fa307c8deb0f544a326fd7df33a52b;p=thirdparty%2Ftornado.git Properly quote and unquote cookie values. Closes #36. --- diff --git a/tornado/test/web_test.py b/tornado/test/web_test.py index 333555fa4..4baeaa142 100644 --- a/tornado/test/web_test.py +++ b/tornado/test/web_test.py @@ -73,11 +73,19 @@ class CookieTest(AsyncHTTPTestCase, LogTrapTestCase): self.set_cookie("unicode_args", "blah", domain=u"foo.com", path=u"/foo") + class SetCookieSpecialCharHandler(RequestHandler): + def get(self): + self.set_cookie("equals", "a=b") + self.set_cookie("semicolon", "a;b") + self.set_cookie("quote", 'a"b') + return Application([ ("/set", SetCookieHandler), ("/get", GetCookieHandler), - ("/set_domain", SetCookieDomainHandler)]) + ("/set_domain", SetCookieDomainHandler), + ("/special_char", SetCookieSpecialCharHandler), + ]) def test_set_cookie(self): response = self.fetch("/set") @@ -90,11 +98,37 @@ class CookieTest(AsyncHTTPTestCase, LogTrapTestCase): response = self.fetch("/get", headers={"Cookie": "foo=bar"}) self.assertEqual(response.body, b("bar")) + response = self.fetch("/get", headers={"Cookie": 'foo="bar"'}) + self.assertEqual(response.body, b("bar")) + def test_set_cookie_domain(self): response = self.fetch("/set_domain") self.assertEqual(response.headers.get_list("Set-Cookie"), ["unicode_args=blah; Domain=foo.com; Path=/foo"]) + def test_cookie_special_char(self): + response = self.fetch("/special_char") + headers = response.headers.get_list("Set-Cookie") + self.assertEqual(len(headers), 3) + self.assertEqual(headers[0], 'equals="a=b"; Path=/') + # python 2.7 octal-escapes the semicolon; older versions leave it alone + self.assertTrue(headers[1] in ('semicolon="a;b"; Path=/', + 'semicolon="a\\073b"; Path=/'), + headers[1]) + self.assertEqual(headers[2], 'quote="a\\"b"; Path=/') + + data = [('foo=a=b', 'a=b'), + ('foo="a=b"', 'a=b'), + ('foo="a;b"', 'a;b'), + #('foo=a\\073b', 'a;b'), # even encoded, ";" is a delimiter + ('foo="a\\073b"', 'a;b'), + ('foo="a\\"b"', 'a"b'), + ] + for header, expected in data: + logging.info("trying %r", header) + response = self.fetch("/get", headers={"Cookie": header}) + self.assertEqual(response.body, utf8(expected)) + class AuthRedirectRequestHandler(RequestHandler): def initialize(self, login_url): self.login_url = login_url diff --git a/tornado/web.py b/tornado/web.py index c5b0ed79d..6e8d14854 100644 --- a/tornado/web.py +++ b/tornado/web.py @@ -293,7 +293,7 @@ class RequestHandler(object): def cookies(self): """A dictionary of Cookie.Morsel objects.""" if not hasattr(self, "_cookies"): - self._cookies = Cookie.BaseCookie() + self._cookies = Cookie.SimpleCookie() if "Cookie" in self.request.headers: try: self._cookies.load( @@ -325,7 +325,7 @@ class RequestHandler(object): raise ValueError("Invalid cookie %r: %r" % (name, value)) if not hasattr(self, "_new_cookies"): self._new_cookies = [] - new_cookie = Cookie.BaseCookie() + new_cookie = Cookie.SimpleCookie() self._new_cookies.append(new_cookie) new_cookie[name] = value if domain: