Mercurial > repos > guerler > hhblits
comparison lib/python3.8/site-packages/pip/_vendor/cachecontrol/heuristics.py @ 0:9e54283cc701 draft
"planemo upload commit d12c32a45bcd441307e632fca6d9af7d60289d44"
author | guerler |
---|---|
date | Mon, 27 Jul 2020 03:47:31 -0400 |
parents | |
children |
comparison
equal
deleted
inserted
replaced
-1:000000000000 | 0:9e54283cc701 |
---|---|
1 import calendar | |
2 import time | |
3 | |
4 from email.utils import formatdate, parsedate, parsedate_tz | |
5 | |
6 from datetime import datetime, timedelta | |
7 | |
8 TIME_FMT = "%a, %d %b %Y %H:%M:%S GMT" | |
9 | |
10 | |
11 def expire_after(delta, date=None): | |
12 date = date or datetime.utcnow() | |
13 return date + delta | |
14 | |
15 | |
16 def datetime_to_header(dt): | |
17 return formatdate(calendar.timegm(dt.timetuple())) | |
18 | |
19 | |
20 class BaseHeuristic(object): | |
21 | |
22 def warning(self, response): | |
23 """ | |
24 Return a valid 1xx warning header value describing the cache | |
25 adjustments. | |
26 | |
27 The response is provided too allow warnings like 113 | |
28 http://tools.ietf.org/html/rfc7234#section-5.5.4 where we need | |
29 to explicitly say response is over 24 hours old. | |
30 """ | |
31 return '110 - "Response is Stale"' | |
32 | |
33 def update_headers(self, response): | |
34 """Update the response headers with any new headers. | |
35 | |
36 NOTE: This SHOULD always include some Warning header to | |
37 signify that the response was cached by the client, not | |
38 by way of the provided headers. | |
39 """ | |
40 return {} | |
41 | |
42 def apply(self, response): | |
43 updated_headers = self.update_headers(response) | |
44 | |
45 if updated_headers: | |
46 response.headers.update(updated_headers) | |
47 warning_header_value = self.warning(response) | |
48 if warning_header_value is not None: | |
49 response.headers.update({"Warning": warning_header_value}) | |
50 | |
51 return response | |
52 | |
53 | |
54 class OneDayCache(BaseHeuristic): | |
55 """ | |
56 Cache the response by providing an expires 1 day in the | |
57 future. | |
58 """ | |
59 | |
60 def update_headers(self, response): | |
61 headers = {} | |
62 | |
63 if "expires" not in response.headers: | |
64 date = parsedate(response.headers["date"]) | |
65 expires = expire_after(timedelta(days=1), date=datetime(*date[:6])) | |
66 headers["expires"] = datetime_to_header(expires) | |
67 headers["cache-control"] = "public" | |
68 return headers | |
69 | |
70 | |
71 class ExpiresAfter(BaseHeuristic): | |
72 """ | |
73 Cache **all** requests for a defined time period. | |
74 """ | |
75 | |
76 def __init__(self, **kw): | |
77 self.delta = timedelta(**kw) | |
78 | |
79 def update_headers(self, response): | |
80 expires = expire_after(self.delta) | |
81 return {"expires": datetime_to_header(expires), "cache-control": "public"} | |
82 | |
83 def warning(self, response): | |
84 tmpl = "110 - Automatically cached for %s. Response might be stale" | |
85 return tmpl % self.delta | |
86 | |
87 | |
88 class LastModified(BaseHeuristic): | |
89 """ | |
90 If there is no Expires header already, fall back on Last-Modified | |
91 using the heuristic from | |
92 http://tools.ietf.org/html/rfc7234#section-4.2.2 | |
93 to calculate a reasonable value. | |
94 | |
95 Firefox also does something like this per | |
96 https://developer.mozilla.org/en-US/docs/Web/HTTP/Caching_FAQ | |
97 http://lxr.mozilla.org/mozilla-release/source/netwerk/protocol/http/nsHttpResponseHead.cpp#397 | |
98 Unlike mozilla we limit this to 24-hr. | |
99 """ | |
100 cacheable_by_default_statuses = { | |
101 200, 203, 204, 206, 300, 301, 404, 405, 410, 414, 501 | |
102 } | |
103 | |
104 def update_headers(self, resp): | |
105 headers = resp.headers | |
106 | |
107 if "expires" in headers: | |
108 return {} | |
109 | |
110 if "cache-control" in headers and headers["cache-control"] != "public": | |
111 return {} | |
112 | |
113 if resp.status not in self.cacheable_by_default_statuses: | |
114 return {} | |
115 | |
116 if "date" not in headers or "last-modified" not in headers: | |
117 return {} | |
118 | |
119 date = calendar.timegm(parsedate_tz(headers["date"])) | |
120 last_modified = parsedate(headers["last-modified"]) | |
121 if date is None or last_modified is None: | |
122 return {} | |
123 | |
124 now = time.time() | |
125 current_age = max(0, now - date) | |
126 delta = date - calendar.timegm(last_modified) | |
127 freshness_lifetime = max(0, min(delta / 10, 24 * 3600)) | |
128 if freshness_lifetime <= current_age: | |
129 return {} | |
130 | |
131 expires = date + freshness_lifetime | |
132 return {"expires": time.strftime(TIME_FMT, time.gmtime(expires))} | |
133 | |
134 def warning(self, resp): | |
135 return None |