]>
jfr.im git - dlqueue.git/blob - venv/lib/python3.11/site-packages/werkzeug/sansio/http.py
1 from __future__
import annotations
5 from datetime
import datetime
7 from .._internal
import _dt_as_utc
8 from ..http
import generate_etag
9 from ..http
import parse_date
10 from ..http
import parse_etags
11 from ..http
import parse_if_range_header
12 from ..http
import unquote_etag
14 _etag_re
= re
.compile(r
'([Ww]/)?(?:"(.*?)"|(.*?))(?:\s*,\s*|$)')
17 def is_resource_modified(
18 http_range
: str |
None = None,
19 http_if_range
: str |
None = None,
20 http_if_modified_since
: str |
None = None,
21 http_if_none_match
: str |
None = None,
22 http_if_match
: str |
None = None,
23 etag
: str |
None = None,
24 data
: bytes |
None = None,
25 last_modified
: datetime |
str |
None = None,
26 ignore_if_range
: bool = True,
28 """Convenience method for conditional requests.
29 :param http_range: Range HTTP header
30 :param http_if_range: If-Range HTTP header
31 :param http_if_modified_since: If-Modified-Since HTTP header
32 :param http_if_none_match: If-None-Match HTTP header
33 :param http_if_match: If-Match HTTP header
34 :param etag: the etag for the response for comparison.
35 :param data: or alternatively the data of the response to automatically
36 generate an etag using :func:`generate_etag`.
37 :param last_modified: an optional date of the last modification.
38 :param ignore_if_range: If `False`, `If-Range` header will be taken into
40 :return: `True` if the resource was modified, otherwise `False`.
44 if etag
is None and data
is not None:
45 etag
= generate_etag(data
)
46 elif data
is not None:
47 raise TypeError("both data and etag given")
50 if isinstance(last_modified
, str):
51 last_modified
= parse_date(last_modified
)
53 # HTTP doesn't use microsecond, remove it to avoid false positive
54 # comparisons. Mark naive datetimes as UTC.
55 if last_modified
is not None:
56 last_modified
= _dt_as_utc(last_modified
.replace(microsecond
=0))
59 if not ignore_if_range
and http_range
is not None:
60 # https://tools.ietf.org/html/rfc7233#section-3.2
61 # A server MUST ignore an If-Range header field received in a request
62 # that does not contain a Range header field.
63 if_range
= parse_if_range_header(http_if_range
)
65 if if_range
is not None and if_range
.date
is not None:
66 modified_since
: datetime |
None = if_range
.date
68 modified_since
= parse_date(http_if_modified_since
)
70 if modified_since
and last_modified
and last_modified
<= modified_since
:
74 etag
, _
= unquote_etag(etag
)
75 etag
= t
.cast(str, etag
)
77 if if_range
is not None and if_range
.etag
is not None:
78 unmodified
= parse_etags(if_range
.etag
).contains(etag
)
80 if_none_match
= parse_etags(http_if_none_match
)
82 # https://tools.ietf.org/html/rfc7232#section-3.2
83 # "A recipient MUST use the weak comparison function when comparing
84 # entity-tags for If-None-Match"
85 unmodified
= if_none_match
.contains_weak(etag
)
87 # https://tools.ietf.org/html/rfc7232#section-3.1
88 # "Origin server MUST use the strong comparison function when
89 # comparing entity-tags for If-Match"
90 if_match
= parse_etags(http_if_match
)
92 unmodified
= not if_match
.is_strong(etag
)
97 _cookie_re
= re
.compile(
109 flags
=re
.ASCII | re
.VERBOSE
,
111 _cookie_unslash_re
= re
.compile(rb
"\\([0-3][0-7]{2}|.)")
114 def _cookie_unslash_replace(m
: t
.Match
[bytes]) -> bytes:
120 return int(v
, 8).to_bytes(1, "big")
124 cookie
: str |
None = None,
125 cls
: type[ds
.MultiDict
] |
None = None,
126 ) -> ds
.MultiDict
[str, str]:
127 """Parse a cookie from a string.
129 The same key can be provided multiple times, the values are stored
130 in-order. The default :class:`MultiDict` will have the first value
131 first, and all values can be retrieved with
132 :meth:`MultiDict.getlist`.
134 :param cookie: The cookie header as a string.
135 :param cls: A dict-like class to store the parsed cookies in.
136 Defaults to :class:`MultiDict`.
138 .. versionchanged:: 3.0
139 Passing bytes, and the ``charset`` and ``errors`` parameters, were removed.
141 .. versionadded:: 2.2
149 cookie
= f
"{cookie};"
152 for ck
, cv
in _cookie_re
.findall(cookie
):
159 if len(cv
) >= 2 and cv
[0] == cv
[-1] == '"':
160 # Work with bytes here, since a UTF-8 character could be multiple bytes.
161 cv
= _cookie_unslash_re
.sub(
162 _cookie_unslash_replace
, cv
[1:-1].encode()
163 ).decode(errors
="replace")
170 # circular dependencies
171 from .. import datastructures
as ds