Coverage for /pythoncovmergedfiles/medio/medio/usr/local/lib/python3.8/site-packages/werkzeug/sansio/http.py: 27%

56 statements  

« prev     ^ index     » next       coverage.py v7.0.1, created at 2022-12-25 06:11 +0000

1import re 

2import typing as t 

3from datetime import datetime 

4 

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 

13 

14_etag_re = re.compile(r'([Ww]/)?(?:"(.*?)"|(.*?))(?:\s*,\s*|$)') 

15 

16 

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`. 

41 

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") 

48 

49 unmodified = False 

50 if isinstance(last_modified, str): 

51 last_modified = parse_date(last_modified) 

52 

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)) 

57 

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) 

64 

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) 

69 

70 if modified_since and last_modified and last_modified <= modified_since: 

71 unmodified = True 

72 

73 if etag: 

74 etag, _ = unquote_etag(etag) 

75 etag = t.cast(str, etag) 

76 

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) 

86 

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) 

93 

94 return not unmodified 

95 

96 

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. 

104 

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`. 

109 

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`. 

115 

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") 

122 

123 if cls is None: 

124 cls = ds.MultiDict 

125 

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 

130 if not key_str: 

131 continue 

132 

133 val_str = _to_str(val, charset, errors, allow_none_charset=True) 

134 yield key_str, val_str 

135 

136 return cls(_parse_pairs()) 

137 

138 

139# circular dependencies 

140from .. import datastructures as ds