feat(set_cookie): Convert max-age value to int implicitly (#711)

Make set_cookie() more robust in handling max-age values, without introducing significant overhead.

Fixes #704
This commit is contained in:
Jan-Philip Gehrcke
2016-04-26 18:24:26 +02:00
committed by Kurt Griffiths
parent 538958713b
commit 296e6430c5
3 changed files with 30 additions and 2 deletions

View File

@@ -60,6 +60,7 @@ below in order of date of first contribution:
* Yohan Boniface (yohanboniface)
* Steven Colby (StevenWColby)
* M Somerville (dracos)
* Jan-Philip Gehrcke (jgehrcke)
* Abhilash Raj (maxking)
* David Larlet (davidbgk)
* Fran Fitzpatrick (fxfitz)

View File

@@ -154,7 +154,8 @@ class Response(object):
default, cookies expire when the user agent exits.
max_age (int): Defines the lifetime of the cookie in seconds.
After the specified number of seconds elapse, the client
should discard the cookie.
should discard the cookie. Coercion to `int` is attempted
if provided with `float` or `str`.
domain (str): Specifies the domain for which the cookie is valid.
An explicitly specified domain must always start with a dot.
A value of 0 means the cookie should be discarded immediately.
@@ -218,7 +219,13 @@ class Response(object):
self._cookies[name]['expires'] = gmt_expires.strftime(fmt)
if max_age:
self._cookies[name]['max-age'] = max_age
# RFC 6265 section 5.2.2 says about the max-age value:
# "If the remainder of attribute-value contains a non-DIGIT
# character, ignore the cookie-av."
# That is, RFC-compliant response parsers will ignore the max-age
# attribute if the value contains a dot, as in floating point
# numbers. Therefore, attempt to convert the value to an integer.
self._cookies[name]['max-age'] = int(max_age)
if domain:
self._cookies[name]['domain'] = domain

View File

@@ -50,6 +50,15 @@ class CookieResource:
resp.unset_cookie('bad')
class CookieResourceMaxAgeFloatString:
def on_get(self, req, resp):
resp.set_cookie(
'foofloat', 'bar', max_age=15.3, secure=False, http_only=False)
resp.set_cookie(
'foostring', 'bar', max_age='15', secure=False, http_only=False)
@ddt.ddt
class TestCookies(testing.TestBase):
@@ -113,6 +122,17 @@ class TestCookies(testing.TestBase):
self.assertEqual(morsel.value, 'bar')
self.assertEqual(morsel['max-age'], 300)
def test_cookie_max_age_float_and_string(self):
# Falcon implicitly converts max-age values to integers,
# for ensuring RFC 6265-compliance of the attribute value.
self.resource = CookieResourceMaxAgeFloatString()
self.api.add_route(self.test_route, self.resource)
self.simulate_request(self.test_route, method='GET')
self.assertIn(
('set-cookie', 'foofloat=bar; Max-Age=15'), self.srmock.headers)
self.assertIn(
('set-cookie', 'foostring=bar; Max-Age=15'), self.srmock.headers)
def test_response_unset_cookie(self):
resp = falcon.Response()
resp.unset_cookie('bad')