aboutsummaryrefslogtreecommitdiff
path: root/venv/lib/python3.8/site-packages/werkzeug/sansio/utils.py
diff options
context:
space:
mode:
Diffstat (limited to 'venv/lib/python3.8/site-packages/werkzeug/sansio/utils.py')
-rw-r--r--venv/lib/python3.8/site-packages/werkzeug/sansio/utils.py159
1 files changed, 159 insertions, 0 deletions
diff --git a/venv/lib/python3.8/site-packages/werkzeug/sansio/utils.py b/venv/lib/python3.8/site-packages/werkzeug/sansio/utils.py
new file mode 100644
index 0000000..14fa0ac
--- /dev/null
+++ b/venv/lib/python3.8/site-packages/werkzeug/sansio/utils.py
@@ -0,0 +1,159 @@
+from __future__ import annotations
+
+import typing as t
+from urllib.parse import quote
+
+from .._internal import _plain_int
+from ..exceptions import SecurityError
+from ..urls import uri_to_iri
+
+
+def host_is_trusted(hostname: str | None, trusted_list: t.Iterable[str]) -> bool:
+ """Check if a host matches a list of trusted names.
+
+ :param hostname: The name to check.
+ :param trusted_list: A list of valid names to match. If a name
+ starts with a dot it will match all subdomains.
+
+ .. versionadded:: 0.9
+ """
+ if not hostname:
+ return False
+
+ try:
+ hostname = hostname.partition(":")[0].encode("idna").decode("ascii")
+ except UnicodeEncodeError:
+ return False
+
+ if isinstance(trusted_list, str):
+ trusted_list = [trusted_list]
+
+ for ref in trusted_list:
+ if ref.startswith("."):
+ ref = ref[1:]
+ suffix_match = True
+ else:
+ suffix_match = False
+
+ try:
+ ref = ref.partition(":")[0].encode("idna").decode("ascii")
+ except UnicodeEncodeError:
+ return False
+
+ if ref == hostname or (suffix_match and hostname.endswith(f".{ref}")):
+ return True
+
+ return False
+
+
+def get_host(
+ scheme: str,
+ host_header: str | None,
+ server: tuple[str, int | None] | None = None,
+ trusted_hosts: t.Iterable[str] | None = None,
+) -> str:
+ """Return the host for the given parameters.
+
+ This first checks the ``host_header``. If it's not present, then
+ ``server`` is used. The host will only contain the port if it is
+ different than the standard port for the protocol.
+
+ Optionally, verify that the host is trusted using
+ :func:`host_is_trusted` and raise a
+ :exc:`~werkzeug.exceptions.SecurityError` if it is not.
+
+ :param scheme: The protocol the request used, like ``"https"``.
+ :param host_header: The ``Host`` header value.
+ :param server: Address of the server. ``(host, port)``, or
+ ``(path, None)`` for unix sockets.
+ :param trusted_hosts: A list of trusted host names.
+
+ :return: Host, with port if necessary.
+ :raise ~werkzeug.exceptions.SecurityError: If the host is not
+ trusted.
+ """
+ host = ""
+
+ if host_header is not None:
+ host = host_header
+ elif server is not None:
+ host = server[0]
+
+ if server[1] is not None:
+ host = f"{host}:{server[1]}"
+
+ if scheme in {"http", "ws"} and host.endswith(":80"):
+ host = host[:-3]
+ elif scheme in {"https", "wss"} and host.endswith(":443"):
+ host = host[:-4]
+
+ if trusted_hosts is not None:
+ if not host_is_trusted(host, trusted_hosts):
+ raise SecurityError(f"Host {host!r} is not trusted.")
+
+ return host
+
+
+def get_current_url(
+ scheme: str,
+ host: str,
+ root_path: str | None = None,
+ path: str | None = None,
+ query_string: bytes | None = None,
+) -> str:
+ """Recreate the URL for a request. If an optional part isn't
+ provided, it and subsequent parts are not included in the URL.
+
+ The URL is an IRI, not a URI, so it may contain Unicode characters.
+ Use :func:`~werkzeug.urls.iri_to_uri` to convert it to ASCII.
+
+ :param scheme: The protocol the request used, like ``"https"``.
+ :param host: The host the request was made to. See :func:`get_host`.
+ :param root_path: Prefix that the application is mounted under. This
+ is prepended to ``path``.
+ :param path: The path part of the URL after ``root_path``.
+ :param query_string: The portion of the URL after the "?".
+ """
+ url = [scheme, "://", host]
+
+ if root_path is None:
+ url.append("/")
+ return uri_to_iri("".join(url))
+
+ # safe = https://url.spec.whatwg.org/#url-path-segment-string
+ # as well as percent for things that are already quoted
+ url.append(quote(root_path.rstrip("/"), safe="!$&'()*+,/:;=@%"))
+ url.append("/")
+
+ if path is None:
+ return uri_to_iri("".join(url))
+
+ url.append(quote(path.lstrip("/"), safe="!$&'()*+,/:;=@%"))
+
+ if query_string:
+ url.append("?")
+ url.append(quote(query_string, safe="!$&'()*+,/:;=?@%"))
+
+ return uri_to_iri("".join(url))
+
+
+def get_content_length(
+ http_content_length: str | None = None,
+ http_transfer_encoding: str | None = None,
+) -> int | None:
+ """Return the ``Content-Length`` header value as an int. If the header is not given
+ or the ``Transfer-Encoding`` header is ``chunked``, ``None`` is returned to indicate
+ a streaming request. If the value is not an integer, or negative, 0 is returned.
+
+ :param http_content_length: The Content-Length HTTP header.
+ :param http_transfer_encoding: The Transfer-Encoding HTTP header.
+
+ .. versionadded:: 2.2
+ """
+ if http_transfer_encoding == "chunked" or http_content_length is None:
+ return None
+
+ try:
+ return max(0, _plain_int(http_content_length))
+ except ValueError:
+ return 0