OSDN Git Service

PEP8対応
[stux/ultron.git] / venv / Lib / site-packages / pip / _vendor / cachecontrol / heuristics.py
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.now()
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     def update_headers(self, response):
60         headers = {}
61
62         if 'expires' not in response.headers:
63             date = parsedate(response.headers['date'])
64             expires = expire_after(timedelta(days=1),
65                                    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 {
82             'expires': datetime_to_header(expires),
83             'cache-control': 'public',
84         }
85
86     def warning(self, response):
87         tmpl = '110 - Automatically cached for %s. Response might be stale'
88         return tmpl % self.delta
89
90
91 class LastModified(BaseHeuristic):
92     """
93     If there is no Expires header already, fall back on Last-Modified
94     using the heuristic from
95     http://tools.ietf.org/html/rfc7234#section-4.2.2
96     to calculate a reasonable value.
97
98     Firefox also does something like this per
99     https://developer.mozilla.org/en-US/docs/Web/HTTP/Caching_FAQ
100     http://lxr.mozilla.org/mozilla-release/source/netwerk/protocol/http/nsHttpResponseHead.cpp#397
101     Unlike mozilla we limit this to 24-hr.
102     """
103     cacheable_by_default_statuses = set([
104         200, 203, 204, 206, 300, 301, 404, 405, 410, 414, 501
105     ])
106
107     def update_headers(self, resp):
108         headers = resp.headers
109
110         if 'expires' in headers:
111             return {}
112
113         if 'cache-control' in headers and headers['cache-control'] != 'public':
114             return {}
115
116         if resp.status not in self.cacheable_by_default_statuses:
117             return {}
118
119         if 'date' not in headers or 'last-modified' not in headers:
120             return {}
121
122         date = calendar.timegm(parsedate_tz(headers['date']))
123         last_modified = parsedate(headers['last-modified'])
124         if date is None or last_modified is None:
125             return {}
126
127         now = time.time()
128         current_age = max(0, now - date)
129         delta = date - calendar.timegm(last_modified)
130         freshness_lifetime = max(0, min(delta / 10, 24 * 3600))
131         if freshness_lifetime <= current_age:
132             return {}
133
134         expires = date + freshness_lifetime
135         return {'expires': time.strftime(TIME_FMT, time.gmtime(expires))}
136
137     def warning(self, resp):
138         return None