Coverage for /pythoncovmergedfiles/medio/medio/usr/local/lib/python3.8/site-packages/werkzeug/sansio/http.py: 28%
54 statements
« prev ^ index » next coverage.py v7.2.7, created at 2023-06-07 06:35 +0000
« prev ^ index » next coverage.py v7.2.7, created at 2023-06-07 06:35 +0000
1import re
2import typing as t
3from datetime import datetime
5from .._internal import _cookie_parse_impl
6from .._internal import _dt_as_utc
7from .._internal import _to_str
8from ..http import generate_etag
9from ..http import parse_date
10from ..http import parse_etags
11from ..http import parse_if_range_header
12from ..http import unquote_etag
14_etag_re = re.compile(r'([Ww]/)?(?:"(.*?)"|(.*?))(?:\s*,\s*|$)')
17def is_resource_modified(
18 http_range: t.Optional[str] = None,
19 http_if_range: t.Optional[str] = None,
20 http_if_modified_since: t.Optional[str] = None,
21 http_if_none_match: t.Optional[str] = None,
22 http_if_match: t.Optional[str] = None,
23 etag: t.Optional[str] = None,
24 data: t.Optional[bytes] = None,
25 last_modified: t.Optional[t.Union[datetime, str]] = None,
26 ignore_if_range: bool = True,
27) -> bool:
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
39 account.
40 :return: `True` if the resource was modified, otherwise `False`.
42 .. versionadded:: 2.2
43 """
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")
49 unmodified = False
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))
58 if_range = None
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: t.Optional[datetime] = if_range.date
67 else:
68 modified_since = parse_date(http_if_modified_since)
70 if modified_since and last_modified and last_modified <= modified_since:
71 unmodified = True
73 if etag:
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)
79 else:
80 if_none_match = parse_etags(http_if_none_match)
81 if 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)
91 if if_match:
92 unmodified = not if_match.is_strong(etag)
94 return not unmodified
97def parse_cookie(
98 cookie: t.Union[bytes, str, None] = "",
99 charset: str = "utf-8",
100 errors: str = "replace",
101 cls: t.Optional[t.Type["ds.MultiDict"]] = None,
102) -> "ds.MultiDict[str, str]":
103 """Parse a cookie from a string.
105 The same key can be provided multiple times, the values are stored
106 in-order. The default :class:`MultiDict` will have the first value
107 first, and all values can be retrieved with
108 :meth:`MultiDict.getlist`.
110 :param cookie: The cookie header as a string.
111 :param charset: The charset for the cookie values.
112 :param errors: The error behavior for the charset decoding.
113 :param cls: A dict-like class to store the parsed cookies in.
114 Defaults to :class:`MultiDict`.
116 .. versionadded:: 2.2
117 """
118 # PEP 3333 sends headers through the environ as latin1 decoded
119 # strings. Encode strings back to bytes for parsing.
120 if isinstance(cookie, str):
121 cookie = cookie.encode("latin1", "replace")
123 if cls is None:
124 cls = ds.MultiDict
126 def _parse_pairs() -> t.Iterator[t.Tuple[str, str]]:
127 for key, val in _cookie_parse_impl(cookie): # type: ignore
128 key_str = _to_str(key, charset, errors, allow_none_charset=True)
129 val_str = _to_str(val, charset, errors, allow_none_charset=True)
130 yield key_str, val_str
132 return cls(_parse_pairs())
135# circular dependencies
136from .. import datastructures as ds