aboutsummaryrefslogtreecommitdiff
path: root/venv/lib/python3.8/site-packages/werkzeug/sansio/request.py
diff options
context:
space:
mode:
Diffstat (limited to 'venv/lib/python3.8/site-packages/werkzeug/sansio/request.py')
-rw-r--r--venv/lib/python3.8/site-packages/werkzeug/sansio/request.py536
1 files changed, 536 insertions, 0 deletions
diff --git a/venv/lib/python3.8/site-packages/werkzeug/sansio/request.py b/venv/lib/python3.8/site-packages/werkzeug/sansio/request.py
new file mode 100644
index 0000000..dd0805d
--- /dev/null
+++ b/venv/lib/python3.8/site-packages/werkzeug/sansio/request.py
@@ -0,0 +1,536 @@
+from __future__ import annotations
+
+import typing as t
+from datetime import datetime
+from urllib.parse import parse_qsl
+
+from ..datastructures import Accept
+from ..datastructures import Authorization
+from ..datastructures import CharsetAccept
+from ..datastructures import ETags
+from ..datastructures import Headers
+from ..datastructures import HeaderSet
+from ..datastructures import IfRange
+from ..datastructures import ImmutableList
+from ..datastructures import ImmutableMultiDict
+from ..datastructures import LanguageAccept
+from ..datastructures import MIMEAccept
+from ..datastructures import MultiDict
+from ..datastructures import Range
+from ..datastructures import RequestCacheControl
+from ..http import parse_accept_header
+from ..http import parse_cache_control_header
+from ..http import parse_date
+from ..http import parse_etags
+from ..http import parse_if_range_header
+from ..http import parse_list_header
+from ..http import parse_options_header
+from ..http import parse_range_header
+from ..http import parse_set_header
+from ..user_agent import UserAgent
+from ..utils import cached_property
+from ..utils import header_property
+from .http import parse_cookie
+from .utils import get_content_length
+from .utils import get_current_url
+from .utils import get_host
+
+
+class Request:
+ """Represents the non-IO parts of a HTTP request, including the
+ method, URL info, and headers.
+
+ This class is not meant for general use. It should only be used when
+ implementing WSGI, ASGI, or another HTTP application spec. Werkzeug
+ provides a WSGI implementation at :cls:`werkzeug.wrappers.Request`.
+
+ :param method: The method the request was made with, such as
+ ``GET``.
+ :param scheme: The URL scheme of the protocol the request used, such
+ as ``https`` or ``wss``.
+ :param server: The address of the server. ``(host, port)``,
+ ``(path, None)`` for unix sockets, or ``None`` if not known.
+ :param root_path: The prefix that the application is mounted under.
+ This is prepended to generated URLs, but is not part of route
+ matching.
+ :param path: The path part of the URL after ``root_path``.
+ :param query_string: The part of the URL after the "?".
+ :param headers: The headers received with the request.
+ :param remote_addr: The address of the client sending the request.
+
+ .. versionchanged:: 3.0
+ The ``charset``, ``url_charset``, and ``encoding_errors`` attributes
+ were removed.
+
+ .. versionadded:: 2.0
+ """
+
+ #: the class to use for `args` and `form`. The default is an
+ #: :class:`~werkzeug.datastructures.ImmutableMultiDict` which supports
+ #: multiple values per key. alternatively it makes sense to use an
+ #: :class:`~werkzeug.datastructures.ImmutableOrderedMultiDict` which
+ #: preserves order or a :class:`~werkzeug.datastructures.ImmutableDict`
+ #: which is the fastest but only remembers the last key. It is also
+ #: possible to use mutable structures, but this is not recommended.
+ #:
+ #: .. versionadded:: 0.6
+ parameter_storage_class: type[MultiDict[str, t.Any]] = ImmutableMultiDict
+
+ #: The type to be used for dict values from the incoming WSGI
+ #: environment. (For example for :attr:`cookies`.) By default an
+ #: :class:`~werkzeug.datastructures.ImmutableMultiDict` is used.
+ #:
+ #: .. versionchanged:: 1.0.0
+ #: Changed to ``ImmutableMultiDict`` to support multiple values.
+ #:
+ #: .. versionadded:: 0.6
+ dict_storage_class: type[MultiDict[str, t.Any]] = ImmutableMultiDict
+
+ #: the type to be used for list values from the incoming WSGI environment.
+ #: By default an :class:`~werkzeug.datastructures.ImmutableList` is used
+ #: (for example for :attr:`access_list`).
+ #:
+ #: .. versionadded:: 0.6
+ list_storage_class: type[list[t.Any]] = ImmutableList
+
+ user_agent_class: type[UserAgent] = UserAgent
+ """The class used and returned by the :attr:`user_agent` property to
+ parse the header. Defaults to
+ :class:`~werkzeug.user_agent.UserAgent`, which does no parsing. An
+ extension can provide a subclass that uses a parser to provide other
+ data.
+
+ .. versionadded:: 2.0
+ """
+
+ #: Valid host names when handling requests. By default all hosts are
+ #: trusted, which means that whatever the client says the host is
+ #: will be accepted.
+ #:
+ #: Because ``Host`` and ``X-Forwarded-Host`` headers can be set to
+ #: any value by a malicious client, it is recommended to either set
+ #: this property or implement similar validation in the proxy (if
+ #: the application is being run behind one).
+ #:
+ #: .. versionadded:: 0.9
+ trusted_hosts: list[str] | None = None
+
+ def __init__(
+ self,
+ method: str,
+ scheme: str,
+ server: tuple[str, int | None] | None,
+ root_path: str,
+ path: str,
+ query_string: bytes,
+ headers: Headers,
+ remote_addr: str | None,
+ ) -> None:
+ #: The method the request was made with, such as ``GET``.
+ self.method = method.upper()
+ #: The URL scheme of the protocol the request used, such as
+ #: ``https`` or ``wss``.
+ self.scheme = scheme
+ #: The address of the server. ``(host, port)``, ``(path, None)``
+ #: for unix sockets, or ``None`` if not known.
+ self.server = server
+ #: The prefix that the application is mounted under, without a
+ #: trailing slash. :attr:`path` comes after this.
+ self.root_path = root_path.rstrip("/")
+ #: The path part of the URL after :attr:`root_path`. This is the
+ #: path used for routing within the application.
+ self.path = "/" + path.lstrip("/")
+ #: The part of the URL after the "?". This is the raw value, use
+ #: :attr:`args` for the parsed values.
+ self.query_string = query_string
+ #: The headers received with the request.
+ self.headers = headers
+ #: The address of the client sending the request.
+ self.remote_addr = remote_addr
+
+ def __repr__(self) -> str:
+ try:
+ url = self.url
+ except Exception as e:
+ url = f"(invalid URL: {e})"
+
+ return f"<{type(self).__name__} {url!r} [{self.method}]>"
+
+ @cached_property
+ def args(self) -> MultiDict[str, str]:
+ """The parsed URL parameters (the part in the URL after the question
+ mark).
+
+ By default an
+ :class:`~werkzeug.datastructures.ImmutableMultiDict`
+ is returned from this function. This can be changed by setting
+ :attr:`parameter_storage_class` to a different type. This might
+ be necessary if the order of the form data is important.
+
+ .. versionchanged:: 2.3
+ Invalid bytes remain percent encoded.
+ """
+ return self.parameter_storage_class(
+ parse_qsl(
+ self.query_string.decode(),
+ keep_blank_values=True,
+ errors="werkzeug.url_quote",
+ )
+ )
+
+ @cached_property
+ def access_route(self) -> list[str]:
+ """If a forwarded header exists this is a list of all ip addresses
+ from the client ip to the last proxy server.
+ """
+ if "X-Forwarded-For" in self.headers:
+ return self.list_storage_class(
+ parse_list_header(self.headers["X-Forwarded-For"])
+ )
+ elif self.remote_addr is not None:
+ return self.list_storage_class([self.remote_addr])
+ return self.list_storage_class()
+
+ @cached_property
+ def full_path(self) -> str:
+ """Requested path, including the query string."""
+ return f"{self.path}?{self.query_string.decode()}"
+
+ @property
+ def is_secure(self) -> bool:
+ """``True`` if the request was made with a secure protocol
+ (HTTPS or WSS).
+ """
+ return self.scheme in {"https", "wss"}
+
+ @cached_property
+ def url(self) -> str:
+ """The full request URL with the scheme, host, root path, path,
+ and query string."""
+ return get_current_url(
+ self.scheme, self.host, self.root_path, self.path, self.query_string
+ )
+
+ @cached_property
+ def base_url(self) -> str:
+ """Like :attr:`url` but without the query string."""
+ return get_current_url(self.scheme, self.host, self.root_path, self.path)
+
+ @cached_property
+ def root_url(self) -> str:
+ """The request URL scheme, host, and root path. This is the root
+ that the application is accessed from.
+ """
+ return get_current_url(self.scheme, self.host, self.root_path)
+
+ @cached_property
+ def host_url(self) -> str:
+ """The request URL scheme and host only."""
+ return get_current_url(self.scheme, self.host)
+
+ @cached_property
+ def host(self) -> str:
+ """The host name the request was made to, including the port if
+ it's non-standard. Validated with :attr:`trusted_hosts`.
+ """
+ return get_host(
+ self.scheme, self.headers.get("host"), self.server, self.trusted_hosts
+ )
+
+ @cached_property
+ def cookies(self) -> ImmutableMultiDict[str, str]:
+ """A :class:`dict` with the contents of all cookies transmitted with
+ the request."""
+ wsgi_combined_cookie = ";".join(self.headers.getlist("Cookie"))
+ return parse_cookie( # type: ignore
+ wsgi_combined_cookie, cls=self.dict_storage_class
+ )
+
+ # Common Descriptors
+
+ content_type = header_property[str](
+ "Content-Type",
+ doc="""The Content-Type entity-header field indicates the media
+ type of the entity-body sent to the recipient or, in the case of
+ the HEAD method, the media type that would have been sent had
+ the request been a GET.""",
+ read_only=True,
+ )
+
+ @cached_property
+ def content_length(self) -> int | None:
+ """The Content-Length entity-header field indicates the size of the
+ entity-body in bytes or, in the case of the HEAD method, the size of
+ the entity-body that would have been sent had the request been a
+ GET.
+ """
+ return get_content_length(
+ http_content_length=self.headers.get("Content-Length"),
+ http_transfer_encoding=self.headers.get("Transfer-Encoding"),
+ )
+
+ content_encoding = header_property[str](
+ "Content-Encoding",
+ doc="""The Content-Encoding entity-header field is used as a
+ modifier to the media-type. When present, its value indicates
+ what additional content codings have been applied to the
+ entity-body, and thus what decoding mechanisms must be applied
+ in order to obtain the media-type referenced by the Content-Type
+ header field.
+
+ .. versionadded:: 0.9""",
+ read_only=True,
+ )
+ content_md5 = header_property[str](
+ "Content-MD5",
+ doc="""The Content-MD5 entity-header field, as defined in
+ RFC 1864, is an MD5 digest of the entity-body for the purpose of
+ providing an end-to-end message integrity check (MIC) of the
+ entity-body. (Note: a MIC is good for detecting accidental
+ modification of the entity-body in transit, but is not proof
+ against malicious attacks.)
+
+ .. versionadded:: 0.9""",
+ read_only=True,
+ )
+ referrer = header_property[str](
+ "Referer",
+ doc="""The Referer[sic] request-header field allows the client
+ to specify, for the server's benefit, the address (URI) of the
+ resource from which the Request-URI was obtained (the
+ "referrer", although the header field is misspelled).""",
+ read_only=True,
+ )
+ date = header_property(
+ "Date",
+ None,
+ parse_date,
+ doc="""The Date general-header field represents the date and
+ time at which the message was originated, having the same
+ semantics as orig-date in RFC 822.
+
+ .. versionchanged:: 2.0
+ The datetime object is timezone-aware.
+ """,
+ read_only=True,
+ )
+ max_forwards = header_property(
+ "Max-Forwards",
+ None,
+ int,
+ doc="""The Max-Forwards request-header field provides a
+ mechanism with the TRACE and OPTIONS methods to limit the number
+ of proxies or gateways that can forward the request to the next
+ inbound server.""",
+ read_only=True,
+ )
+
+ def _parse_content_type(self) -> None:
+ if not hasattr(self, "_parsed_content_type"):
+ self._parsed_content_type = parse_options_header(
+ self.headers.get("Content-Type", "")
+ )
+
+ @property
+ def mimetype(self) -> str:
+ """Like :attr:`content_type`, but without parameters (eg, without
+ charset, type etc.) and always lowercase. For example if the content
+ type is ``text/HTML; charset=utf-8`` the mimetype would be
+ ``'text/html'``.
+ """
+ self._parse_content_type()
+ return self._parsed_content_type[0].lower()
+
+ @property
+ def mimetype_params(self) -> dict[str, str]:
+ """The mimetype parameters as dict. For example if the content
+ type is ``text/html; charset=utf-8`` the params would be
+ ``{'charset': 'utf-8'}``.
+ """
+ self._parse_content_type()
+ return self._parsed_content_type[1]
+
+ @cached_property
+ def pragma(self) -> HeaderSet:
+ """The Pragma general-header field is used to include
+ implementation-specific directives that might apply to any recipient
+ along the request/response chain. All pragma directives specify
+ optional behavior from the viewpoint of the protocol; however, some
+ systems MAY require that behavior be consistent with the directives.
+ """
+ return parse_set_header(self.headers.get("Pragma", ""))
+
+ # Accept
+
+ @cached_property
+ def accept_mimetypes(self) -> MIMEAccept:
+ """List of mimetypes this client supports as
+ :class:`~werkzeug.datastructures.MIMEAccept` object.
+ """
+ return parse_accept_header(self.headers.get("Accept"), MIMEAccept)
+
+ @cached_property
+ def accept_charsets(self) -> CharsetAccept:
+ """List of charsets this client supports as
+ :class:`~werkzeug.datastructures.CharsetAccept` object.
+ """
+ return parse_accept_header(self.headers.get("Accept-Charset"), CharsetAccept)
+
+ @cached_property
+ def accept_encodings(self) -> Accept:
+ """List of encodings this client accepts. Encodings in a HTTP term
+ are compression encodings such as gzip. For charsets have a look at
+ :attr:`accept_charset`.
+ """
+ return parse_accept_header(self.headers.get("Accept-Encoding"))
+
+ @cached_property
+ def accept_languages(self) -> LanguageAccept:
+ """List of languages this client accepts as
+ :class:`~werkzeug.datastructures.LanguageAccept` object.
+
+ .. versionchanged 0.5
+ In previous versions this was a regular
+ :class:`~werkzeug.datastructures.Accept` object.
+ """
+ return parse_accept_header(self.headers.get("Accept-Language"), LanguageAccept)
+
+ # ETag
+
+ @cached_property
+ def cache_control(self) -> RequestCacheControl:
+ """A :class:`~werkzeug.datastructures.RequestCacheControl` object
+ for the incoming cache control headers.
+ """
+ cache_control = self.headers.get("Cache-Control")
+ return parse_cache_control_header(cache_control, None, RequestCacheControl)
+
+ @cached_property
+ def if_match(self) -> ETags:
+ """An object containing all the etags in the `If-Match` header.
+
+ :rtype: :class:`~werkzeug.datastructures.ETags`
+ """
+ return parse_etags(self.headers.get("If-Match"))
+
+ @cached_property
+ def if_none_match(self) -> ETags:
+ """An object containing all the etags in the `If-None-Match` header.
+
+ :rtype: :class:`~werkzeug.datastructures.ETags`
+ """
+ return parse_etags(self.headers.get("If-None-Match"))
+
+ @cached_property
+ def if_modified_since(self) -> datetime | None:
+ """The parsed `If-Modified-Since` header as a datetime object.
+
+ .. versionchanged:: 2.0
+ The datetime object is timezone-aware.
+ """
+ return parse_date(self.headers.get("If-Modified-Since"))
+
+ @cached_property
+ def if_unmodified_since(self) -> datetime | None:
+ """The parsed `If-Unmodified-Since` header as a datetime object.
+
+ .. versionchanged:: 2.0
+ The datetime object is timezone-aware.
+ """
+ return parse_date(self.headers.get("If-Unmodified-Since"))
+
+ @cached_property
+ def if_range(self) -> IfRange:
+ """The parsed ``If-Range`` header.
+
+ .. versionchanged:: 2.0
+ ``IfRange.date`` is timezone-aware.
+
+ .. versionadded:: 0.7
+ """
+ return parse_if_range_header(self.headers.get("If-Range"))
+
+ @cached_property
+ def range(self) -> Range | None:
+ """The parsed `Range` header.
+
+ .. versionadded:: 0.7
+
+ :rtype: :class:`~werkzeug.datastructures.Range`
+ """
+ return parse_range_header(self.headers.get("Range"))
+
+ # User Agent
+
+ @cached_property
+ def user_agent(self) -> UserAgent:
+ """The user agent. Use ``user_agent.string`` to get the header
+ value. Set :attr:`user_agent_class` to a subclass of
+ :class:`~werkzeug.user_agent.UserAgent` to provide parsing for
+ the other properties or other extended data.
+
+ .. versionchanged:: 2.1
+ The built-in parser was removed. Set ``user_agent_class`` to a ``UserAgent``
+ subclass to parse data from the string.
+ """
+ return self.user_agent_class(self.headers.get("User-Agent", ""))
+
+ # Authorization
+
+ @cached_property
+ def authorization(self) -> Authorization | None:
+ """The ``Authorization`` header parsed into an :class:`.Authorization` object.
+ ``None`` if the header is not present.
+
+ .. versionchanged:: 2.3
+ :class:`Authorization` is no longer a ``dict``. The ``token`` attribute
+ was added for auth schemes that use a token instead of parameters.
+ """
+ return Authorization.from_header(self.headers.get("Authorization"))
+
+ # CORS
+
+ origin = header_property[str](
+ "Origin",
+ doc=(
+ "The host that the request originated from. Set"
+ " :attr:`~CORSResponseMixin.access_control_allow_origin` on"
+ " the response to indicate which origins are allowed."
+ ),
+ read_only=True,
+ )
+
+ access_control_request_headers = header_property(
+ "Access-Control-Request-Headers",
+ load_func=parse_set_header,
+ doc=(
+ "Sent with a preflight request to indicate which headers"
+ " will be sent with the cross origin request. Set"
+ " :attr:`~CORSResponseMixin.access_control_allow_headers`"
+ " on the response to indicate which headers are allowed."
+ ),
+ read_only=True,
+ )
+
+ access_control_request_method = header_property[str](
+ "Access-Control-Request-Method",
+ doc=(
+ "Sent with a preflight request to indicate which method"
+ " will be used for the cross origin request. Set"
+ " :attr:`~CORSResponseMixin.access_control_allow_methods`"
+ " on the response to indicate which methods are allowed."
+ ),
+ read_only=True,
+ )
+
+ @property
+ def is_json(self) -> bool:
+ """Check if the mimetype indicates JSON data, either
+ :mimetype:`application/json` or :mimetype:`application/*+json`.
+ """
+ mt = self.mimetype
+ return (
+ mt == "application/json"
+ or mt.startswith("application/")
+ and mt.endswith("+json")
+ )