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
|
import typing as t
from .._internal import _encode_idna
from ..exceptions import SecurityError
from ..urls import uri_to_iri
from ..urls import url_quote
def host_is_trusted(hostname: str, 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
if isinstance(trusted_list, str):
trusted_list = [trusted_list]
def _normalize(hostname: str) -> bytes:
if ":" in hostname:
hostname = hostname.rsplit(":", 1)[0]
return _encode_idna(hostname)
try:
hostname_bytes = _normalize(hostname)
except UnicodeError:
return False
for ref in trusted_list:
if ref.startswith("."):
ref = ref[1:]
suffix_match = True
else:
suffix_match = False
try:
ref_bytes = _normalize(ref)
except UnicodeError:
return False
if ref_bytes == hostname_bytes:
return True
if suffix_match and hostname_bytes.endswith(b"." + ref_bytes):
return True
return False
def get_host(
scheme: str,
host_header: t.Optional[str],
server: t.Optional[t.Tuple[str, t.Optional[int]]] = None,
trusted_hosts: t.Optional[t.Iterable[str]] = 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: t.Optional[str] = None,
path: t.Optional[str] = None,
query_string: t.Optional[bytes] = 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))
url.append(url_quote(root_path.rstrip("/")))
url.append("/")
if path is None:
return uri_to_iri("".join(url))
url.append(url_quote(path.lstrip("/")))
if query_string:
url.append("?")
url.append(url_quote(query_string, safe=":&%=+$!*'(),"))
return uri_to_iri("".join(url))
|