✨ 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.decorators import csrf_exempt
|
||||||
from spiderweb.main import SpiderwebRouter
|
from spiderweb.main import SpiderwebRouter
|
||||||
from spiderweb.exceptions import ServerError
|
from spiderweb.exceptions import ServerError
|
||||||
@ -67,6 +69,21 @@ def form(request):
|
|||||||
else:
|
else:
|
||||||
return TemplateResponse(request, "form.html")
|
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__":
|
if __name__ == "__main__":
|
||||||
# can also add routes like this:
|
# can also add routes like this:
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
[tool.poetry]
|
[tool.poetry]
|
||||||
name = "spiderweb"
|
name = "spiderweb"
|
||||||
version = "0.8.0"
|
version = "0.9.0"
|
||||||
description = "A small web framework, just big enough to hold your average spider."
|
description = "A small web framework, just big enough to hold your average spider."
|
||||||
authors = ["Joe Kaufeld <opensource@joekaufeld.com>"]
|
authors = ["Joe Kaufeld <opensource@joekaufeld.com>"]
|
||||||
readme = "README.md"
|
readme = "README.md"
|
||||||
|
@ -1,3 +1,6 @@
|
|||||||
DEFAULT_ALLOWED_METHODS = ["GET"]
|
DEFAULT_ALLOWED_METHODS = ["GET"]
|
||||||
DEFAULT_ENCODING = "ISO-8859-1"
|
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):
|
def fire_response(self, start_response, request: Request, resp: HttpResponse):
|
||||||
try:
|
try:
|
||||||
status = get_http_status_by_code(resp.status_code)
|
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())
|
headers = list(resp.headers.items())
|
||||||
|
for c in cookies:
|
||||||
|
headers.append(("Set-Cookie", c))
|
||||||
|
|
||||||
start_response(status, headers)
|
start_response(status, headers)
|
||||||
|
|
||||||
|
@ -26,9 +26,11 @@ class Request:
|
|||||||
self.GET = {}
|
self.GET = {}
|
||||||
self.POST = {}
|
self.POST = {}
|
||||||
self.META = {}
|
self.META = {}
|
||||||
|
self.COOKIES = {}
|
||||||
|
|
||||||
self.populate_headers()
|
self.populate_headers()
|
||||||
self.populate_meta()
|
self.populate_meta()
|
||||||
|
self.populate_cookies()
|
||||||
|
|
||||||
content_length = int(self.headers.get("CONTENT_LENGTH") or 0)
|
content_length = int(self.headers.get("CONTENT_LENGTH") or 0)
|
||||||
if content_length:
|
if content_length:
|
||||||
@ -63,6 +65,10 @@ class Request:
|
|||||||
for f in fields:
|
for f in fields:
|
||||||
self.META[f] = self.environ.get(f)
|
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):
|
def json(self):
|
||||||
return json.loads(self.content)
|
return json.loads(self.content)
|
||||||
|
|
||||||
|
@ -1,10 +1,12 @@
|
|||||||
import datetime
|
import datetime
|
||||||
import json
|
import json
|
||||||
|
import re
|
||||||
from typing import Any
|
from typing import Any
|
||||||
|
import urllib.parse
|
||||||
import mimetypes
|
import mimetypes
|
||||||
from wsgiref.util import FileWrapper
|
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.exceptions import GeneralException
|
||||||
from spiderweb.request import Request
|
from spiderweb.request import Request
|
||||||
|
|
||||||
@ -36,6 +38,63 @@ class HttpResponse:
|
|||||||
def __str__(self):
|
def __str__(self):
|
||||||
return self.body
|
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:
|
def render(self) -> str:
|
||||||
return str(self.body)
|
return str(self.body)
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user