summaryrefslogtreecommitdiff
path: root/venv/lib/python3.7/site-packages/pip-10.0.1-py3.7.egg/pip/_vendor/cachecontrol/controller.py
diff options
context:
space:
mode:
authorShubham Saini <shubham6405@gmail.com>2019-08-05 08:32:33 +0000
committerShubham Saini <shubham6405@gmail.com>2019-08-05 08:32:33 +0000
commit227b2d30a8675b44918f9d9ca89b24144a938215 (patch)
tree9f8e6a28724514b6fdf463a9ab2067a7ef309b72 /venv/lib/python3.7/site-packages/pip-10.0.1-py3.7.egg/pip/_vendor/cachecontrol/controller.py
parent842a8cfbbbdb1f92889d892e4859dbd5d40c5be8 (diff)
removing venv files
Diffstat (limited to 'venv/lib/python3.7/site-packages/pip-10.0.1-py3.7.egg/pip/_vendor/cachecontrol/controller.py')
-rw-r--r--venv/lib/python3.7/site-packages/pip-10.0.1-py3.7.egg/pip/_vendor/cachecontrol/controller.py373
1 files changed, 0 insertions, 373 deletions
diff --git a/venv/lib/python3.7/site-packages/pip-10.0.1-py3.7.egg/pip/_vendor/cachecontrol/controller.py b/venv/lib/python3.7/site-packages/pip-10.0.1-py3.7.egg/pip/_vendor/cachecontrol/controller.py
deleted file mode 100644
index bf4cc7f..0000000
--- a/venv/lib/python3.7/site-packages/pip-10.0.1-py3.7.egg/pip/_vendor/cachecontrol/controller.py
+++ /dev/null
@@ -1,373 +0,0 @@
1"""
2The httplib2 algorithms ported for use with requests.
3"""
4import logging
5import re
6import calendar
7import time
8from email.utils import parsedate_tz
9
10from pip._vendor.requests.structures import CaseInsensitiveDict
11
12from .cache import DictCache
13from .serialize import Serializer
14
15
16logger = logging.getLogger(__name__)
17
18URI = re.compile(r"^(([^:/?#]+):)?(//([^/?#]*))?([^?#]*)(\?([^#]*))?(#(.*))?")
19
20
21def parse_uri(uri):
22 """Parses a URI using the regex given in Appendix B of RFC 3986.
23
24 (scheme, authority, path, query, fragment) = parse_uri(uri)
25 """
26 groups = URI.match(uri).groups()
27 return (groups[1], groups[3], groups[4], groups[6], groups[8])
28
29
30class CacheController(object):
31 """An interface to see if request should cached or not.
32 """
33 def __init__(self, cache=None, cache_etags=True, serializer=None,
34 status_codes=None):
35 self.cache = cache or DictCache()
36 self.cache_etags = cache_etags
37 self.serializer = serializer or Serializer()
38 self.cacheable_status_codes = status_codes or (200, 203, 300, 301)
39
40 @classmethod
41 def _urlnorm(cls, uri):
42 """Normalize the URL to create a safe key for the cache"""
43 (scheme, authority, path, query, fragment) = parse_uri(uri)
44 if not scheme or not authority:
45 raise Exception("Only absolute URIs are allowed. uri = %s" % uri)
46
47 scheme = scheme.lower()
48 authority = authority.lower()
49
50 if not path:
51 path = "/"
52
53 # Could do syntax based normalization of the URI before
54 # computing the digest. See Section 6.2.2 of Std 66.
55 request_uri = query and "?".join([path, query]) or path
56 defrag_uri = scheme + "://" + authority + request_uri
57
58 return defrag_uri
59
60 @classmethod
61 def cache_url(cls, uri):
62 return cls._urlnorm(uri)
63
64 def parse_cache_control(self, headers):
65 known_directives = {
66 # https://tools.ietf.org/html/rfc7234#section-5.2
67 'max-age': (int, True,),
68 'max-stale': (int, False,),
69 'min-fresh': (int, True,),
70 'no-cache': (None, False,),
71 'no-store': (None, False,),
72 'no-transform': (None, False,),
73 'only-if-cached' : (None, False,),
74 'must-revalidate': (None, False,),
75 'public': (None, False,),
76 'private': (None, False,),
77 'proxy-revalidate': (None, False,),
78 's-maxage': (int, True,)
79 }
80
81 cc_headers = headers.get('cache-control',
82 headers.get('Cache-Control', ''))
83
84 retval = {}
85
86 for cc_directive in cc_headers.split(','):
87 parts = cc_directive.split('=', 1)
88 directive = parts[0].strip()
89
90 try:
91 typ, required = known_directives[directive]
92 except KeyError:
93 logger.debug('Ignoring unknown cache-control directive: %s',
94 directive)
95 continue
96
97 if not typ or not required:
98 retval[directive] = None
99 if typ:
100 try:
101 retval[directive] = typ(parts[1].strip())
102 except IndexError:
103 if required:
104 logger.debug('Missing value for cache-control '
105 'directive: %s', directive)
106 except ValueError:
107 logger.debug('Invalid value for cache-control directive '
108 '%s, must be %s', directive, typ.__name__)
109
110 return retval
111
112 def cached_request(self, request):
113 """
114 Return a cached response if it exists in the cache, otherwise
115 return False.
116 """
117 cache_url = self.cache_url(request.url)
118 logger.debug('Looking up "%s" in the cache', cache_url)
119 cc = self.parse_cache_control(request.headers)
120
121 # Bail out if the request insists on fresh data
122 if 'no-cache' in cc:
123 logger.debug('Request header has "no-cache", cache bypassed')
124 return False
125
126 if 'max-age' in cc and cc['max-age'] == 0:
127 logger.debug('Request header has "max_age" as 0, cache bypassed')
128 return False
129
130 # Request allows serving from the cache, let's see if we find something
131 cache_data = self.cache.get(cache_url)
132 if cache_data is None:
133 logger.debug('No cache entry available')
134 return False
135
136 # Check whether it can be deserialized
137 resp = self.serializer.loads(request, cache_data)
138 if not resp:
139 logger.warning('Cache entry deserialization failed, entry ignored')
140 return False
141
142 # If we have a cached 301, return it immediately. We don't
143 # need to test our response for other headers b/c it is
144 # intrinsically "cacheable" as it is Permanent.
145 # See:
146 # https://tools.ietf.org/html/rfc7231#section-6.4.2
147 #
148 # Client can try to refresh the value by repeating the request
149 # with cache busting headers as usual (ie no-cache).
150 if resp.status == 301:
151 msg = ('Returning cached "301 Moved Permanently" response '
152 '(ignoring date and etag information)')
153 logger.debug(msg)
154 return resp
155
156 headers = CaseInsensitiveDict(resp.headers)
157 if not headers or 'date' not in headers:
158 if 'etag' not in headers:
159 # Without date or etag, the cached response can never be used
160 # and should be deleted.
161 logger.debug('Purging cached response: no date or etag')
162 self.cache.delete(cache_url)
163 logger.debug('Ignoring cached response: no date')
164 return False
165
166 now = time.time()
167 date = calendar.timegm(
168 parsedate_tz(headers['date'])
169 )
170 current_age = max(0, now - date)
171 logger.debug('Current age based on date: %i', current_age)
172
173 # TODO: There is an assumption that the result will be a
174 # urllib3 response object. This may not be best since we
175 # could probably avoid instantiating or constructing the
176 # response until we know we need it.
177 resp_cc = self.parse_cache_control(headers)
178
179 # determine freshness
180 freshness_lifetime = 0
181
182 # Check the max-age pragma in the cache control header
183 if 'max-age' in resp_cc:
184 freshness_lifetime = resp_cc['max-age']
185 logger.debug('Freshness lifetime from max-age: %i',
186 freshness_lifetime)
187
188 # If there isn't a max-age, check for an expires header
189 elif 'expires' in headers:
190 expires = parsedate_tz(headers['expires'])
191 if expires is not None:
192 expire_time = calendar.timegm(expires) - date
193 freshness_lifetime = max(0, expire_time)
194 logger.debug("Freshness lifetime from expires: %i",
195 freshness_lifetime)
196
197 # Determine if we are setting freshness limit in the
198 # request. Note, this overrides what was in the response.
199 if 'max-age' in cc:
200 freshness_lifetime = cc['max-age']
201 logger.debug('Freshness lifetime from request max-age: %i',
202 freshness_lifetime)
203
204 if 'min-fresh' in cc:
205 min_fresh = cc['min-fresh']
206 # adjust our current age by our min fresh
207 current_age += min_fresh
208 logger.debug('Adjusted current age from min-fresh: %i',
209 current_age)
210
211 # Return entry if it is fresh enough
212 if freshness_lifetime > current_age:
213 logger.debug('The response is "fresh", returning cached response')
214 logger.debug('%i > %i', freshness_lifetime, current_age)
215 return resp
216
217 # we're not fresh. If we don't have an Etag, clear it out
218 if 'etag' not in headers:
219 logger.debug(
220 'The cached response is "stale" with no etag, purging'
221 )
222 self.cache.delete(cache_url)
223
224 # return the original handler
225 return False
226
227 def conditional_headers(self, request):
228 cache_url = self.cache_url(request.url)
229 resp = self.serializer.loads(request, self.cache.get(cache_url))
230 new_headers = {}
231
232 if resp:
233 headers = CaseInsensitiveDict(resp.headers)
234
235 if 'etag' in headers:
236 new_headers['If-None-Match'] = headers['ETag']
237
238 if 'last-modified' in headers:
239 new_headers['If-Modified-Since'] = headers['Last-Modified']
240
241 return new_headers
242
243 def cache_response(self, request, response, body=None,
244 status_codes=None):
245 """
246 Algorithm for caching requests.
247
248 This assumes a requests Response object.
249 """
250 # From httplib2: Don't cache 206's since we aren't going to
251 # handle byte range requests
252 cacheable_status_codes = status_codes or self.cacheable_status_codes
253 if response.status not in cacheable_status_codes:
254 logger.debug(
255 'Status code %s not in %s',
256 response.status,
257 cacheable_status_codes
258 )
259 return
260
261 response_headers = CaseInsensitiveDict(response.headers)
262
263 # If we've been given a body, our response has a Content-Length, that
264 # Content-Length is valid then we can check to see if the body we've
265 # been given matches the expected size, and if it doesn't we'll just
266 # skip trying to cache it.
267 if (body is not None and
268 "content-length" in response_headers and
269 response_headers["content-length"].isdigit() and
270 int(response_headers["content-length"]) != len(body)):
271 return
272
273 cc_req = self.parse_cache_control(request.headers)
274 cc = self.parse_cache_control(response_headers)
275
276 cache_url = self.cache_url(request.url)
277 logger.debug('Updating cache with response from "%s"', cache_url)
278
279 # Delete it from the cache if we happen to have it stored there
280 no_store = False
281 if 'no-store' in cc:
282 no_store = True
283 logger.debug('Response header has "no-store"')
284 if 'no-store' in cc_req:
285 no_store = True
286 logger.debug('Request header has "no-store"')
287 if no_store and self.cache.get(cache_url):
288 logger.debug('Purging existing cache entry to honor "no-store"')
289 self.cache.delete(cache_url)
290
291 # If we've been given an etag, then keep the response
292 if self.cache_etags and 'etag' in response_headers:
293 logger.debug('Caching due to etag')
294 self.cache.set(
295 cache_url,
296 self.serializer.dumps(request, response, body=body),
297 )
298
299 # Add to the cache any 301s. We do this before looking that
300 # the Date headers.
301 elif response.status == 301:
302 logger.debug('Caching permanant redirect')
303 self.cache.set(
304 cache_url,
305 self.serializer.dumps(request, response)
306 )
307
308 # Add to the cache if the response headers demand it. If there
309 # is no date header then we can't do anything about expiring
310 # the cache.
311 elif 'date' in response_headers:
312 # cache when there is a max-age > 0
313 if 'max-age' in cc and cc['max-age'] > 0:
314 logger.debug('Caching b/c date exists and max-age > 0')
315 self.cache.set(
316 cache_url,
317 self.serializer.dumps(request, response, body=body),
318 )
319
320 # If the request can expire, it means we should cache it
321 # in the meantime.
322 elif 'expires' in response_headers:
323 if response_headers['expires']:
324 logger.debug('Caching b/c of expires header')
325 self.cache.set(
326 cache_url,
327 self.serializer.dumps(request, response, body=body),
328 )
329
330 def update_cached_response(self, request, response):
331 """On a 304 we will get a new set of headers that we want to
332 update our cached value with, assuming we have one.
333
334 This should only ever be called when we've sent an ETag and
335 gotten a 304 as the response.
336 """
337 cache_url = self.cache_url(request.url)
338
339 cached_response = self.serializer.loads(
340 request,
341 self.cache.get(cache_url)
342 )
343
344 if not cached_response:
345 # we didn't have a cached response
346 return response
347
348 # Lets update our headers with the headers from the new request:
349 # http://tools.ietf.org/html/draft-ietf-httpbis-p4-conditional-26#section-4.1
350 #
351 # The server isn't supposed to send headers that would make
352 # the cached body invalid. But... just in case, we'll be sure
353 # to strip out ones we know that might be problmatic due to
354 # typical assumptions.
355 excluded_headers = [
356 "content-length",
357 ]
358
359 cached_response.headers.update(
360 dict((k, v) for k, v in response.headers.items()
361 if k.lower() not in excluded_headers)
362 )
363
364 # we want a 200 b/c we have content via the cache
365 cached_response.status = 200
366
367 # update our cache
368 self.cache.set(
369 cache_url,
370 self.serializer.dumps(request, cached_response),
371 )
372
373 return cached_response