7 import arvados.collection
10 def my_formatdate(dt):
11 return email.utils.formatdate(timeval=time.mktime(now.timetuple()), localtime=False, usegmt=True)
13 def my_parsedate(text):
14 return datetime.datetime(*email.utils.parsedate(text)[:6])
16 def fresh_cache(url, properties):
20 if "Cache-Control" in pr:
21 if re.match(r"immutable", pr["Cache-Control"]):
24 g = re.match(r"(s-maxage|max-age)=(\d+)", pr["Cache-Control"])
26 expires = my_parsedate(pr["Date"]) + datetime.timedelta(seconds=int(g.group(2)))
28 if expires is None and "Expires" in pr:
29 expires = my_parsedate(pr["Expires"])
34 return (datetime.datetime.utcnow() < expires)
36 def remember_headers(url, properties, headers):
37 properties.setdefault(url, {})
38 for h in ("Cache-Control", "ETag", "Expires", "Date"):
40 properties[url][h] = headers[h]
41 if "Date" not in headers:
42 properties[url]["Date"] = my_formatdate(datetime.datetime.utcnow())
45 def changed(url, properties):
46 req = requests.head(url)
47 remember_headers(url, properties, req.headers)
49 if req.status_code != 200:
50 raise Exception("Got status %s" % req.status_code)
53 if "ETag" in pr and "ETag" in req.headers:
54 if pr["ETag"] == req.headers["ETag"]:
58 def http_to_keep(api, project_uuid, url):
59 r = api.collections().list(filters=[["properties", "exists", url]]).execute()
60 name = urlparse.urlparse(url).path.split("/")[-1]
62 for item in r["items"]:
63 properties = item["properties"]
64 if fresh_cache(url, properties):
66 return "keep:%s/%s" % (item["portable_data_hash"], name)
68 if not changed(url, properties):
69 # ETag didn't change, same content, just update headers
70 api.collections().update(uuid=item["uuid"], body={"collection":{"properties": properties}}).execute()
71 return "keep:%s/%s" % (item["portable_data_hash"], name)
74 req = requests.get(url, stream=True)
76 if req.status_code != 200:
77 raise Exception("Got status %s" % req.status_code)
79 remember_headers(url, properties, req.headers)
81 c = arvados.collection.Collection()
83 with c.open(name, "w") as f:
84 for chunk in req.iter_content(chunk_size=128):
87 c.save_new(name="Downloaded from %s" % url, owner_uuid=project_uuid)
89 api.collections().update(uuid=c.manifest_locator(), body={"collection":{"properties": properties}}).execute()
91 return "keep:%s/%s" % (c.portable_data_hash(), name)