Spaces:
Running
Running
File size: 5,256 Bytes
47b2311 |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 |
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.
.. versionchanged:: 3.1.3
If ``SERVER_NAME`` is IPv6, it is wrapped in ``[]``.
"""
host = ""
if host_header is not None:
host = host_header
elif server is not None:
host = server[0]
# If SERVER_NAME is IPv6, wrap it in [] to match Host header.
# Check for : because domain or IPv4 can't have that.
if ":" in host and host[0] != "[":
host = f"[{host}]"
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
|