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_F
| 95 | |
| 96 | |
| 97 | class LastModified(BaseHeuristic): |
| 98 | """ |
| 99 | If there is no Expires header already, fall back on Last-Modified |
| 100 | using the heuristic from |
| 101 | http://tools.ietf.org/html/rfc7234#section-4.2.2 |
| 102 | to calculate a reasonable value. |
| 103 | |
| 104 | Firefox also does something like this per |
| 105 | https://developer.mozilla.org/en-US/docs/Web/HTTP/Caching_FAQ |
| 106 | http://lxr.mozilla.org/mozilla-release/source/netwerk/protocol/http/nsHttpResponseHead.cpp#397 |
| 107 | Unlike mozilla we limit this to 24-hr. |
| 108 | """ |
| 109 | |
| 110 | cacheable_by_default_statuses = { |
| 111 | 200, |
| 112 | 203, |
| 113 | 204, |
| 114 | 206, |
| 115 | 300, |
| 116 | 301, |
| 117 | 404, |
| 118 | 405, |
| 119 | 410, |
| 120 | 414, |
| 121 | 501, |
| 122 | } |
| 123 | |
| 124 | def update_headers(self, resp: HTTPResponse) -> dict[str, str]: |
| 125 | headers: Mapping[str, str] = resp.headers |
| 126 | |
| 127 | if "expires" in headers: |
| 128 | return {} |
| 129 | |
| 130 | if "cache-control" in headers and headers["cache-control"] != "public": |
| 131 | return {} |
| 132 | |
| 133 | if resp.status not in self.cacheable_by_default_statuses: |
| 134 | return {} |
| 135 | |
| 136 | if "date" not in headers or "last-modified" not in headers: |
| 137 | return {} |
| 138 | |
| 139 | time_tuple = parsedate_tz(headers["date"]) |
| 140 | assert time_tuple is not None |
| 141 | date = calendar.timegm(time_tuple[:6]) |
| 142 | last_modified = parsedate(headers["last-modified"]) |
| 143 | if last_modified is None: |
| 144 | return {} |
| 145 | |
| 146 | now = time.time() |
| 147 | current_age = max(0, now - date) |
| 148 | delta = date - calendar.timegm(last_modified) |
| 149 | freshness_lifetime = max(0, min(delta / 10, 24 * 3600)) |
| 150 | if freshness_lifetime <= current_age: |
| 151 | return {} |
| 152 | |
| 153 | expires = date + freshness_lifetime |
| 154 | return {"expires": time.strftime(TIME_FMT, time.gmtime(expires))} |
no outgoing calls
searching dependent graphs…