Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 12 additions & 2 deletions bottle.py
Original file line number Diff line number Diff line change
Expand Up @@ -128,7 +128,7 @@ def getargspec(func):
from urllib.parse import urljoin, SplitResult as UrlSplitResult
from urllib.parse import urlencode, quote as urlquote, unquote as urlunquote
urlunquote = functools.partial(urlunquote, encoding='latin1')
from http.cookies import SimpleCookie
from http.cookies import SimpleCookie, Morsel, CookieError
from collections import MutableMapping as DictMixin
import pickle
from io import BytesIO
Expand Down Expand Up @@ -1813,6 +1813,10 @@ def set_cookie(self, name, value, secret=None, digestmod=hashlib.sha256, **optio
:param secure: limit the cookie to HTTPS connections (default: off).
:param httponly: prevents client-side javascript to read this cookie
(default: off, requires Python 2.6 or newer).
:param same_site: disables third-party use for a cookie.
Allowed attributes: `lax` and `strict`.
In strict mode the cookie will never be sent.
In lax mode the cookie is only sent with a top-level GET request.

If neither `expires` nor `max_age` is set (default), the cookie will
expire at the end of the browser session (as soon as the browser
Expand All @@ -1834,7 +1838,10 @@ def set_cookie(self, name, value, secret=None, digestmod=hashlib.sha256, **optio
"""
if not self._cookies:
self._cookies = SimpleCookie()


# To add "SameSite" cookie support.
Morsel._reserved['same-site'] = 'SameSite'

if secret:
if not isinstance(value, basestring):
depr(0, 13, "Pickling of arbitrary objects into cookies is "
Expand Down Expand Up @@ -1863,6 +1870,9 @@ def set_cookie(self, name, value, secret=None, digestmod=hashlib.sha256, **optio
elif isinstance(value, (int, float)):
value = time.gmtime(value)
value = time.strftime("%a, %d %b %Y %H:%M:%S GMT", value)
# check values for SameSite cookie, because it's not natively supported by http.cookies.
if key == 'same_site' and value.lower() not in ('lax', 'strict'):
raise CookieError("Invalid attribute %r" % (key,))
if key in ('secure', 'httponly') and not value:
continue
self._cookies[name][key.replace('_', '-')] = value
Expand Down
1 change: 1 addition & 0 deletions docs/tutorial.rst
Original file line number Diff line number Diff line change
Expand Up @@ -420,6 +420,7 @@ The :meth:`Response.set_cookie` method accepts a number of additional keyword ar
* **path:** Limit the cookie to a given path (default: ``/``)
* **secure:** Limit the cookie to HTTPS connections (default: off).
* **httponly:** Prevent client-side javascript to read this cookie (default: off, requires Python 2.7 or newer).
* **same_site:** Disables third-party use for a cookie. Allowed attributes: `lax` and `strict`. In strict mode the cookie will never be sent. In lax mode the cookie is only sent with a top-level GET request.

If neither `expires` nor `max_age` is set, the cookie expires at the end of the browser session or as soon as the browser window is closed. There are some other gotchas you should consider when using cookies:

Expand Down