codekingpro/portable-devtools
114k
1from __future__ import annotations2 3import typing as t4from urllib.parse import quote5 6from .._internal import _plain_int7from ..exceptions import SecurityError8from ..urls import uri_to_iri9 10 11def host_is_trusted(hostname: str | None, trusted_list: t.Iterable[str]) -> bool:12 """Check if a host matches a list of trusted names.13 14 :param hostname: The name to check.15 :param trusted_list: A list of valid names to match. If a name16 starts with a dot it will match all subdomains.17 18 .. versionadded:: 0.919 """20 if not hostname:21 return False22 23 try:24 hostname = hostname.partition(":")[0].encode("idna").decode("ascii")25 except UnicodeEncodeError:26 return False27 28 if isinstance(trusted_list, str):29 trusted_list = [trusted_list]30 31 for ref in trusted_list:32 if ref.startswith("."):33 ref = ref[1:]34 suffix_match = True35 else:36 suffix_match = False37 38 try:39 ref = ref.partition(":")[0].encode("idna").decode("ascii")40 except UnicodeEncodeError:41 return False42 43 if ref == hostname or (suffix_match and hostname.endswith(f".{ref}")):44 return True45 46 return False47 48 49def get_host(50 scheme: str,51 host_header: str | None,52 server: tuple[str, int | None] | None = None,53 trusted_hosts: t.Iterable[str] | None = None,54) -> str:55 """Return the host for the given parameters.56 57 This first checks the ``host_header``. If it's not present, then58 ``server`` is used. The host will only contain the port if it is59 different than the standard port for the protocol.60 61 Optionally, verify that the host is trusted using62 :func:`host_is_trusted` and raise a63 :exc:`~werkzeug.exceptions.SecurityError` if it is not.64 65 :param scheme: The protocol the request used, like ``"https"``.66 :param host_header: The ``Host`` header value.67 :param server: Address of the server. ``(host, port)``, or68 ``(path, None)`` for unix sockets.69 :param trusted_hosts: A list of trusted host names.70 71 :return: Host, with port if necessary.72 :raise ~werkzeug.exceptions.SecurityError: If the host is not73 trusted.74 """75 host = ""76 77 if host_header is not None:78 host = host_header79 elif server is not None:80 host = server[0]81 82 if server[1] is not None:83 host = f"{host}:{server[1]}"84 85 if scheme in {"http", "ws"} and host.endswith(":80"):86 host = host[:-3]87 elif scheme in {"https", "wss"} and host.endswith(":443"):88 host = host[:-4]89 90 if trusted_hosts is not None:91 if not host_is_trusted(host, trusted_hosts):92 raise SecurityError(f"Host {host!r} is not trusted.")93 94 return host95 96 97def get_current_url(98 scheme: str,99 host: str,100 root_path: str | None = None,101 path: str | None = None,102 query_string: bytes | None = None,103) -> str:104 """Recreate the URL for a request. If an optional part isn't105 provided, it and subsequent parts are not included in the URL.106 107 The URL is an IRI, not a URI, so it may contain Unicode characters.108 Use :func:`~werkzeug.urls.iri_to_uri` to convert it to ASCII.109 110 :param scheme: The protocol the request used, like ``"https"``.111 :param host: The host the request was made to. See :func:`get_host`.112 :param root_path: Prefix that the application is mounted under. This113 is prepended to ``path``.114 :param path: The path part of the URL after ``root_path``.115 :param query_string: The portion of the URL after the "?".116 """117 url = [scheme, "://", host]118 119 if root_path is None:120 url.append("/")121 return uri_to_iri("".join(url))122 123 # safe = https://url.spec.whatwg.org/#url-path-segment-string124 # as well as percent for things that are already quoted125 url.append(quote(root_path.rstrip("/"), safe="!$&'()*+,/:;=@%"))126 url.append("/")127 128 if path is None:129 return uri_to_iri("".join(url))130 131 url.append(quote(path.lstrip("/"), safe="!$&'()*+,/:;=@%"))132 133 if query_string:134 url.append("?")135 url.append(quote(query_string, safe="!$&'()*+,/:;=?@%"))136 137 return uri_to_iri("".join(url))138 139 140def get_content_length(141 http_content_length: str | None = None,142 http_transfer_encoding: str | None = None,143) -> int | None:144 """Return the ``Content-Length`` header value as an int. If the header is not given145 or the ``Transfer-Encoding`` header is ``chunked``, ``None`` is returned to indicate146 a streaming request. If the value is not an integer, or negative, 0 is returned.147 148 :param http_content_length: The Content-Length HTTP header.149 :param http_transfer_encoding: The Transfer-Encoding HTTP header.150 151 .. versionadded:: 2.2152 """153 if http_transfer_encoding == "chunked" or http_content_length is None:154 return None155 156 try:157 return max(0, _plain_int(http_content_length))158 except ValueError:159 return 0160 