✨ add cookie support
This commit is contained in:
parent
14e7ad72ee
commit
62f3d650bc
17
example.py
17
example.py
@ -1,3 +1,5 @@
|
||||
from datetime import datetime, timedelta
|
||||
|
||||
from spiderweb.decorators import csrf_exempt
|
||||
from spiderweb.main import SpiderwebRouter
|
||||
from spiderweb.exceptions import ServerError
|
||||
@ -67,6 +69,21 @@ def form(request):
|
||||
else:
|
||||
return TemplateResponse(request, "form.html")
|
||||
|
||||
@app.route("/cookies")
|
||||
def cookies(request):
|
||||
print("request.COOKIES: ", request.COOKIES)
|
||||
resp = HttpResponse(body="COOKIES! NOM NOM NOM")
|
||||
resp.set_cookie(name='nom', value="everyonelovescookies")
|
||||
resp.set_cookie(name="nom2", value="seriouslycookies")
|
||||
resp.set_cookie(
|
||||
name="nom3",
|
||||
value="yumyum",
|
||||
partitioned=True,
|
||||
expires=datetime.utcnow()+timedelta(seconds=10),
|
||||
max_age=15
|
||||
)
|
||||
return resp
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
# can also add routes like this:
|
||||
|
@ -1,6 +1,6 @@
|
||||
[tool.poetry]
|
||||
name = "spiderweb"
|
||||
version = "0.8.0"
|
||||
version = "0.9.0"
|
||||
description = "A small web framework, just big enough to hold your average spider."
|
||||
authors = ["Joe Kaufeld <opensource@joekaufeld.com>"]
|
||||
readme = "README.md"
|
||||
|
@ -1,3 +1,6 @@
|
||||
DEFAULT_ALLOWED_METHODS = ["GET"]
|
||||
DEFAULT_ENCODING = "ISO-8859-1"
|
||||
__version__ = "0.8.0"
|
||||
__version__ = "0.9.0"
|
||||
|
||||
# https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Set-Cookie
|
||||
REGEX_COOKIE_NAME = r'^[a-zA-Z0-9\s\(\)<>@,;:\/\\\[\]\?=\{\}\"\t]*$'
|
||||
|
@ -92,7 +92,13 @@ class SpiderwebRouter(
|
||||
def fire_response(self, start_response, request: Request, resp: HttpResponse):
|
||||
try:
|
||||
status = get_http_status_by_code(resp.status_code)
|
||||
cookies = []
|
||||
if "Set-Cookie" in resp.headers:
|
||||
cookies = resp.headers['Set-Cookie']
|
||||
del resp.headers['Set-Cookie']
|
||||
headers = list(resp.headers.items())
|
||||
for c in cookies:
|
||||
headers.append(("Set-Cookie", c))
|
||||
|
||||
start_response(status, headers)
|
||||
|
||||
|
@ -26,9 +26,11 @@ class Request:
|
||||
self.GET = {}
|
||||
self.POST = {}
|
||||
self.META = {}
|
||||
self.COOKIES = {}
|
||||
|
||||
self.populate_headers()
|
||||
self.populate_meta()
|
||||
self.populate_cookies()
|
||||
|
||||
content_length = int(self.headers.get("CONTENT_LENGTH") or 0)
|
||||
if content_length:
|
||||
@ -63,6 +65,10 @@ class Request:
|
||||
for f in fields:
|
||||
self.META[f] = self.environ.get(f)
|
||||
|
||||
def populate_cookies(self) -> None:
|
||||
if cookies := self.environ.get("HTTP_COOKIE"):
|
||||
self.COOKIES = {l.split("=")[0]: l.split("=")[1] for l in cookies.split("; ")}
|
||||
|
||||
def json(self):
|
||||
return json.loads(self.content)
|
||||
|
||||
|
@ -1,10 +1,12 @@
|
||||
import datetime
|
||||
import json
|
||||
import re
|
||||
from typing import Any
|
||||
import urllib.parse
|
||||
import mimetypes
|
||||
from wsgiref.util import FileWrapper
|
||||
|
||||
from spiderweb.constants import DEFAULT_ENCODING
|
||||
from spiderweb.constants import REGEX_COOKIE_NAME
|
||||
from spiderweb.exceptions import GeneralException
|
||||
from spiderweb.request import Request
|
||||
|
||||
@ -36,6 +38,63 @@ class HttpResponse:
|
||||
def __str__(self):
|
||||
return self.body
|
||||
|
||||
def set_cookie(
|
||||
self,
|
||||
name: str,
|
||||
value: str,
|
||||
domain: str=None,
|
||||
expires: datetime.datetime = None,
|
||||
http_only: bool=None,
|
||||
max_age: int=None,
|
||||
partitioned: bool=None,
|
||||
path: str=None,
|
||||
secure: bool=False,
|
||||
same_site: str=None
|
||||
):
|
||||
if not bool(re.match(REGEX_COOKIE_NAME, name)):
|
||||
url = "https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Set-Cookie#attributes"
|
||||
raise GeneralException(
|
||||
f"Cookie name has illegal characters. See {url} for information on"
|
||||
f" allowed characters."
|
||||
)
|
||||
additions = {}
|
||||
booleans = []
|
||||
|
||||
if domain:
|
||||
additions["Domain"] = domain
|
||||
if expires:
|
||||
additions["Expires"] = expires.strftime("%a, %d %b %Y %H:%M:%S GMT")
|
||||
if max_age:
|
||||
additions["Max-Age"] = int(max_age)
|
||||
if path:
|
||||
additions["Path"] = path
|
||||
if same_site:
|
||||
valid_values = ["strict", "lax", "none"]
|
||||
if same_site.lower() not in valid_values:
|
||||
raise GeneralException(
|
||||
f"Invalid value {same_site} for `same_site` cookie attribute. Valid"
|
||||
f" options are 'strict', 'lax', or 'none'."
|
||||
)
|
||||
additions["SameSite"] = same_site.title()
|
||||
|
||||
if http_only:
|
||||
booleans.append("HttpOnly")
|
||||
if partitioned:
|
||||
booleans.append("Partitioned")
|
||||
if secure:
|
||||
booleans.append("Secure")
|
||||
|
||||
attrs = [f"{k}={v}" for k, v in additions.items()]
|
||||
attrs += booleans
|
||||
attrs = [urllib.parse.quote_plus(value)] + attrs
|
||||
cookie = f"{name}={'; '.join(attrs)}"
|
||||
|
||||
if "Set-Cookie" in self.headers:
|
||||
self.headers["Set-Cookie"].append(cookie)
|
||||
else:
|
||||
self.headers["Set-Cookie"] = [cookie]
|
||||
|
||||
|
||||
def render(self) -> str:
|
||||
return str(self.body)
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user