# Copyright 2015 Oliver Cope
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
from datetime import datetime, timedelta
from typing import Any
from urllib.parse import quote, unquote
[docs]class Cookie(object):
"""
Represents an HTTP cookie.
See rfc2109, HTTP State Management Mechanism
Example:
>>> from fresco.cookie import Cookie
>>> c = Cookie('session_id', 'abc123')
>>> c.path = '/cgi-bin'
>>> c.domain = '.example.org'
>>> c.path
'/cgi-bin'
>>> str(c)
'session_id=abc123;Domain=.example.org;Path=/cgi-bin;SameSite=Lax'
"""
attributes = [
("Comment", "comment"),
("Domain", "domain"),
("Expires", "expires"),
("Max-Age", "max_age"),
("Path", "path"),
("Secure", "secure"),
("HttpOnly", "httponly"),
("SameSite", "samesite"),
]
bool_attributes = set(["secure", "httponly"])
def __init__(
self,
name,
value,
max_age=None,
expires=None,
path="/",
secure=False,
domain=None,
comment=None,
httponly=False,
samesite="Lax",
version=1,
):
"""
Initialize a ``Cookie`` instance.
"""
self.name = name
self.value = value
self.max_age = max_age
self.path = path
self.secure = secure
self.domain = domain
self.comment = comment
self.version = version
self.expires = expires
self.httponly = httponly
self.samesite = samesite
def __str__(self):
"""
Returns a string representation of the cookie in the format, eg
``session_id=abc123;Path=/cgi-bin;Domain=.example.com;HttpOnly``
"""
cookie = ["%s=%s" % (self.name, quote(str(self.value)))]
append = cookie.append
for cookie_name, att_name in self.attributes:
value = getattr(self, att_name, None)
if att_name in self.bool_attributes:
if value:
append(cookie_name)
elif value is not None:
if att_name == "expires":
value = format_date(value.utctimetuple())
append("%s=%s" % (cookie_name, str(value)))
return ";".join(cookie)
[docs] def set_expires(self, dt):
"""
Set the cookie ``expires`` value to ``datetime`` object ``dt``
"""
if dt is not None and not isinstance(dt, datetime):
raise ValueError("%r is not a datetime" % (dt,))
self._expires = dt
[docs] def get_expires(self):
"""
Return the cookie ``expires`` value as an instance of ``datetime``.
"""
if self._expires is None and self.max_age is not None:
if self.max_age == 0:
# Make sure immediately expiring cookies get a date firmly in
# the past.
self._expires = datetime(1980, 1, 1)
else:
self._expires = datetime.now() + timedelta(seconds=self.max_age)
return self._expires
expires = property(get_expires, set_expires)
[docs]def expire_cookie(cookie_or_name: Any, *args, **kwargs):
"""
Synopsis:
>>> from fresco import Response
>>> from fresco.cookie import expire_cookie
>>> def view():
... return Response(set_cookie=expire_cookie('X', path='/'))
...
>>> from fresco import FrescoApp
>>> with FrescoApp().requestcontext() as c:
... print(view().get_header('Set-Cookie'))
...
X=;Expires=Tue, 01 Jan 1980 00:00:00 GMT;Max-Age=0;Path=/;SameSite=Lax
"""
if isinstance(cookie_or_name, Cookie):
expire = cookie_or_name
else:
expire = Cookie(cookie_or_name, "", *args, **kwargs)
return Cookie(
name=expire.name,
value="",
expires=datetime(1980, 1, 1),
max_age=0,
domain=kwargs.get("domain", expire.domain),
path=kwargs.get("path", expire.path),
)