import calendar import time from email.utils import formatdate, parsedate, parsedate_tz from datetime import datetime, timedelta TIME_FMT = "%a, %d %b %Y %H:%M:%S GMT" def expire_after(delta, date=None): date = date or datetime.utcnow() return date + delta def datetime_to_header(dt): return formatdate(calendar.timegm(dt.timetuple())) class BaseHeuristic(object): def warning(self, response): """ Return a valid 1xx warning header value describing the cache adjustments. The response is provided too allow warnings like 113 http://tools.ietf.org/html/rfc7234#section-5.5.4 where we need to explicitly say response is over 24 hours old. """ return '110 - "Response is Stale"' def update_headers(self, response): """Update the response headers with any new headers. NOTE: This SHOULD always include some Warning header to signify that the response was cached by the client, not by way of the provided headers. """ return {} def apply(self, response): updated_headers = self.update_headers(response) if updated_headers: response.headers.update(updated_headers) warning_header_value = self.warning(response) if warning_header_value is not None: response.headers.update({"Warning": warning_header_value}) return response class OneDayCache(BaseHeuristic): """ Cache the response by providing an expires 1 day in the future. """ def update_headers(self, response): headers = {} if "expires" not in response.headers: date = parsedate(response.headers["date"]) expires = expire_after(timedelta(days=1), date=datetime(*date[:6])) headers["expires"] = datetime_to_header(expires) headers["cache-control"] = "public" return headers class ExpiresAfter(BaseHeuristic): """ Cache **all** requests for a defined time period. """ def __init__(self, **kw): self.delta = timedelta(**kw) def update_headers(self, response): expires = expire_after(self.delta) return {"expires": datetime_to_header(expires), "cache-control": "public"} def warning(self, response): tmpl = "110 - Automatically cached for %s. Response might be stale" return tmpl % self.delta class LastModified(BaseHeuristic): """ If there is no Expires header already, fall back on Last-Modified using the heuristic from http://tools.ietf.org/html/rfc7234#section-4.2.2 to calculate a reasonable value. Firefox also does something like this per https://developer.mozilla.org/en-US/docs/Web/HTTP/Caching_FAQ http://lxr.mozilla.org/mozilla-release/source/netwerk/protocol/http/nsHttpResponseHead.cpp#397 Unlike mozilla we limit this to 24-hr. """ cacheable_by_default_statuses = { 200, 203, 204, 206, 300, 301, 404, 405, 410, 414, 501 } def update_headers(self, resp): headers = resp.headers if "expires" in headers: return {} if "cache-control" in headers and headers["cache-control"] != "public": return {} if resp.status not in self.cacheable_by_default_statuses: return {} if "date" not in headers or "last-modified" not in headers: return {} date = calendar.timegm(parsedate_tz(headers["date"])) last_modified = parsedate(headers["last-modified"]) if date is None or last_modified is None: return {} now = time.time() current_age = max(0, now - date) delta = date - calendar.timegm(last_modified) freshness_lifetime = max(0, min(delta / 10, 24 * 3600)) if freshness_lifetime <= current_age: return {} expires = date + freshness_lifetime return {"expires": time.strftime(TIME_FMT, time.gmtime(expires))} def warning(self, resp): return None
Name | Type | Size | Permission | Actions |
---|---|---|---|---|
__pycache__ | Folder | 0755 |
|
|
caches | Folder | 0755 |
|
|
.__init__.pyo.40009 | File | 596 B | 0644 |
|
._cmd.pyo.40009 | File | 2.07 KB | 0644 |
|
.adapter.pyo.40009 | File | 3.73 KB | 0644 |
|
.cache.pyo.40009 | File | 2.49 KB | 0644 |
|
.compat.pyo.40009 | File | 815 B | 0644 |
|
.controller.pyo.40009 | File | 9.6 KB | 0644 |
|
.filewrapper.pyo.40009 | File | 2.71 KB | 0644 |
|
.heuristics.pyo.40009 | File | 5.99 KB | 0644 |
|
.serialize.pyo.40009 | File | 5.75 KB | 0644 |
|
.wrapper.pyo.40009 | File | 855 B | 0644 |
|
__init__.py | File | 302 B | 0644 |
|
__init__.pyc | File | 596 B | 0644 |
|
__init__.pyo | File | 596 B | 0644 |
|
_cmd.py | File | 1.26 KB | 0644 |
|
_cmd.pyc | File | 2.07 KB | 0644 |
|
_cmd.pyo | File | 2.07 KB | 0644 |
|
adapter.py | File | 4.77 KB | 0644 |
|
adapter.pyc | File | 3.73 KB | 0644 |
|
adapter.pyo | File | 3.73 KB | 0644 |
|
cache.py | File | 805 B | 0644 |
|
cache.pyc | File | 2.49 KB | 0644 |
|
cache.pyo | File | 2.49 KB | 0644 |
|
compat.py | File | 695 B | 0644 |
|
compat.pyc | File | 815 B | 0644 |
|
compat.pyo | File | 815 B | 0644 |
|
controller.py | File | 13.82 KB | 0644 |
|
controller.pyc | File | 9.6 KB | 0644 |
|
controller.pyo | File | 9.6 KB | 0644 |
|
filewrapper.py | File | 2.47 KB | 0644 |
|
filewrapper.pyc | File | 2.71 KB | 0644 |
|
filewrapper.pyo | File | 2.71 KB | 0644 |
|
heuristics.py | File | 3.97 KB | 0644 |
|
heuristics.pyc | File | 5.99 KB | 0644 |
|
heuristics.pyo | File | 5.99 KB | 0644 |
|
serialize.py | File | 6.92 KB | 0644 |
|
serialize.pyc | File | 5.75 KB | 0644 |
|
serialize.pyo | File | 5.75 KB | 0644 |
|
wrapper.py | File | 690 B | 0644 |
|
wrapper.pyc | File | 855 B | 0644 |
|
wrapper.pyo | File | 855 B | 0644 |
|