| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171 | 
							- from __future__ import annotations
 
- import re
 
- import typing as t
 
- from datetime import datetime
 
- from .._internal import _dt_as_utc
 
- from ..http import generate_etag
 
- from ..http import parse_date
 
- from ..http import parse_etags
 
- from ..http import parse_if_range_header
 
- from ..http import unquote_etag
 
- _etag_re = re.compile(r'([Ww]/)?(?:"(.*?)"|(.*?))(?:\s*,\s*|$)')
 
- def is_resource_modified(
 
-     http_range: str | None = None,
 
-     http_if_range: str | None = None,
 
-     http_if_modified_since: str | None = None,
 
-     http_if_none_match: str | None = None,
 
-     http_if_match: str | None = None,
 
-     etag: str | None = None,
 
-     data: bytes | None = None,
 
-     last_modified: datetime | str | None = None,
 
-     ignore_if_range: bool = True,
 
- ) -> bool:
 
-     """Convenience method for conditional requests.
 
-     :param http_range: Range HTTP header
 
-     :param http_if_range: If-Range HTTP header
 
-     :param http_if_modified_since: If-Modified-Since HTTP header
 
-     :param http_if_none_match: If-None-Match HTTP header
 
-     :param http_if_match: If-Match HTTP header
 
-     :param etag: the etag for the response for comparison.
 
-     :param data: or alternatively the data of the response to automatically
 
-                  generate an etag using :func:`generate_etag`.
 
-     :param last_modified: an optional date of the last modification.
 
-     :param ignore_if_range: If `False`, `If-Range` header will be taken into
 
-                             account.
 
-     :return: `True` if the resource was modified, otherwise `False`.
 
-     .. versionadded:: 2.2
 
-     """
 
-     if etag is None and data is not None:
 
-         etag = generate_etag(data)
 
-     elif data is not None:
 
-         raise TypeError("both data and etag given")
 
-     unmodified = False
 
-     if isinstance(last_modified, str):
 
-         last_modified = parse_date(last_modified)
 
-     # HTTP doesn't use microsecond, remove it to avoid false positive
 
-     # comparisons. Mark naive datetimes as UTC.
 
-     if last_modified is not None:
 
-         last_modified = _dt_as_utc(last_modified.replace(microsecond=0))
 
-     if_range = None
 
-     if not ignore_if_range and http_range is not None:
 
-         # https://tools.ietf.org/html/rfc7233#section-3.2
 
-         # A server MUST ignore an If-Range header field received in a request
 
-         # that does not contain a Range header field.
 
-         if_range = parse_if_range_header(http_if_range)
 
-     if if_range is not None and if_range.date is not None:
 
-         modified_since: datetime | None = if_range.date
 
-     else:
 
-         modified_since = parse_date(http_if_modified_since)
 
-     if modified_since and last_modified and last_modified <= modified_since:
 
-         unmodified = True
 
-     if etag:
 
-         etag, _ = unquote_etag(etag)
 
-         etag = t.cast(str, etag)
 
-         if if_range is not None and if_range.etag is not None:
 
-             unmodified = parse_etags(if_range.etag).contains(etag)
 
-         else:
 
-             if_none_match = parse_etags(http_if_none_match)
 
-             if if_none_match:
 
-                 # https://tools.ietf.org/html/rfc7232#section-3.2
 
-                 # "A recipient MUST use the weak comparison function when comparing
 
-                 # entity-tags for If-None-Match"
 
-                 unmodified = if_none_match.contains_weak(etag)
 
-             # https://tools.ietf.org/html/rfc7232#section-3.1
 
-             # "Origin server MUST use the strong comparison function when
 
-             # comparing entity-tags for If-Match"
 
-             if_match = parse_etags(http_if_match)
 
-             if if_match:
 
-                 unmodified = not if_match.is_strong(etag)
 
-     return not unmodified
 
- _cookie_re = re.compile(
 
-     r"""
 
-     ([^=;]*)
 
-     (?:\s*=\s*
 
-       (
 
-         "(?:[^\\"]|\\.)*"
 
-       |
 
-         .*?
 
-       )
 
-     )?
 
-     \s*;\s*
 
-     """,
 
-     flags=re.ASCII | re.VERBOSE,
 
- )
 
- _cookie_unslash_re = re.compile(rb"\\([0-3][0-7]{2}|.)")
 
- def _cookie_unslash_replace(m: t.Match[bytes]) -> bytes:
 
-     v = m.group(1)
 
-     if len(v) == 1:
 
-         return v
 
-     return int(v, 8).to_bytes(1, "big")
 
- def parse_cookie(
 
-     cookie: str | None = None,
 
-     cls: type[ds.MultiDict] | None = None,
 
- ) -> ds.MultiDict[str, str]:
 
-     """Parse a cookie from a string.
 
-     The same key can be provided multiple times, the values are stored
 
-     in-order. The default :class:`MultiDict` will have the first value
 
-     first, and all values can be retrieved with
 
-     :meth:`MultiDict.getlist`.
 
-     :param cookie: The cookie header as a string.
 
-     :param cls: A dict-like class to store the parsed cookies in.
 
-         Defaults to :class:`MultiDict`.
 
-     .. versionchanged:: 3.0
 
-         Passing bytes, and the ``charset`` and ``errors`` parameters, were removed.
 
-     .. versionadded:: 2.2
 
-     """
 
-     if cls is None:
 
-         cls = ds.MultiDict
 
-     if not cookie:
 
-         return cls()
 
-     cookie = f"{cookie};"
 
-     out = []
 
-     for ck, cv in _cookie_re.findall(cookie):
 
-         ck = ck.strip()
 
-         cv = cv.strip()
 
-         if not ck:
 
-             continue
 
-         if len(cv) >= 2 and cv[0] == cv[-1] == '"':
 
-             # Work with bytes here, since a UTF-8 character could be multiple bytes.
 
-             cv = _cookie_unslash_re.sub(
 
-                 _cookie_unslash_replace, cv[1:-1].encode()
 
-             ).decode(errors="replace")
 
-         out.append((ck, cv))
 
-     return cls(out)
 
- # circular dependencies
 
- from .. import datastructures as ds
 
 
  |