106 lines
3.4 KiB
Python
106 lines
3.4 KiB
Python
import json
|
|
from urllib.parse import urlparse
|
|
|
|
from spiderweb.constants import DEFAULT_ENCODING
|
|
from spiderweb.utils import get_client_address, Headers
|
|
|
|
|
|
class Request:
|
|
def __init__(
|
|
self,
|
|
environ=None,
|
|
content=None,
|
|
headers=None,
|
|
path=None,
|
|
server=None,
|
|
handler=None,
|
|
):
|
|
self.environ = environ
|
|
self.content: str = content
|
|
self.method: str = environ["REQUEST_METHOD"]
|
|
self.headers: dict[str, str] = headers if headers else {}
|
|
self.path: str = path if path else environ["PATH_INFO"]
|
|
self.url = urlparse(path)
|
|
self.query_params = []
|
|
self.server = server
|
|
self.handler = handler # the view function that will be called
|
|
self.GET = {}
|
|
self.POST = {}
|
|
self.META = {}
|
|
self.COOKIES = {}
|
|
# only used for the session middleware
|
|
self.SESSION = {}
|
|
self._session: dict = {"new_session": False, "id": None}
|
|
# only used for the pydantic middleware and only on POST requests
|
|
self.validated_data = {}
|
|
|
|
self.populate_headers()
|
|
self.populate_meta()
|
|
self.populate_cookies()
|
|
|
|
content_length = int(self.headers.get("content_length") or 0)
|
|
if content_length:
|
|
self.content = (
|
|
self.environ["wsgi.input"].read(content_length).decode(DEFAULT_ENCODING)
|
|
)
|
|
|
|
def populate_headers(self) -> None:
|
|
data = self.headers
|
|
data |= {
|
|
"content_type": self.environ.get("CONTENT_TYPE"),
|
|
"content_length": self.environ.get("CONTENT_LENGTH"),
|
|
}
|
|
for k, v in self.environ.items():
|
|
if k.startswith("HTTP_"):
|
|
data[k] = v
|
|
self.headers = Headers(**{k.lower(): v for k, v in data.items()})
|
|
|
|
def populate_meta(self) -> None:
|
|
# all caps fields are from WSGI, lowercase names
|
|
# are custom
|
|
fields = [
|
|
"SERVER_PROTOCOL",
|
|
"SERVER_SOFTWARE",
|
|
"REQUEST_METHOD",
|
|
"PATH_INFO",
|
|
"QUERY_STRING",
|
|
"REMOTE_HOST",
|
|
"REMOTE_ADDR",
|
|
"SERVER_NAME",
|
|
"GATEWAY_INTERFACE",
|
|
"SERVER_PORT",
|
|
"CONTENT_LENGTH",
|
|
"SCRIPT_NAME",
|
|
]
|
|
for f in fields:
|
|
self.META[f] = self.environ.get(f)
|
|
for f in self.environ.keys():
|
|
if f.startswith("HTTP_"):
|
|
self.META[f] = self.environ[f]
|
|
self.META["client_address"] = get_client_address(self.environ)
|
|
|
|
def populate_cookies(self) -> None:
|
|
cookies_header = self.environ.get("HTTP_COOKIE")
|
|
if not cookies_header:
|
|
return
|
|
cookies: dict[str, str] = {}
|
|
# Split on ';' and be tolerant of optional spaces and malformed segments
|
|
for segment in cookies_header.split(";"):
|
|
part = segment.strip()
|
|
if not part:
|
|
continue
|
|
if "=" not in part:
|
|
# Ignore flag-like segments that don't conform to name=value
|
|
continue
|
|
name, _, value = part.partition("=") # only split on first '='
|
|
cookies[name.strip()] = value.strip()
|
|
self.COOKIES = cookies
|
|
|
|
def json(self):
|
|
return json.loads(self.content)
|
|
|
|
def is_form_request(self) -> bool:
|
|
return (
|
|
"content_type" in self.headers
|
|
and self.headers["content_type"] == "application/x-www-form-urlencoded"
|
|
)
|