forked from smooth80/defold
-
Notifications
You must be signed in to change notification settings - Fork 2
/
Copy pathhttp_cache.py
95 lines (87 loc) · 3.02 KB
/
http_cache.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
import sys, os, os.path, glob, urlparse, urllib2
from urllib2 import HTTPError
def mangle(url):
url = urlparse.urlparse(url)
#return '%s%s' % (url.hostname.replace('.', '_'), url.path.replace('/', '-'))
return 'defold%s' % url.path.replace('/', '-') # we avoid putting the possibly secret url in the output messages
def log(msg):
print msg
sys.stdout.flush()
sys.stderr.flush()
class Cache(object):
def __init__(self, root, max_size):
self.root = os.path.expanduser(root)
if not os.path.exists(self.root):
os.makedirs(self.root)
self.max_size = max_size
def _url_to_path(self, url):
return os.path.join(self.root, mangle(url))
def get(self, url):
path = self._url_to_path(url)
pattern = '%s-*' % (path)
matches = glob.glob(pattern)
if matches:
match = matches[0]
if match.endswith('_tmp'):
os.remove(match)
return None
key = match.rsplit('-', 1)[1]
os.utime(match, None)
return (match, key.decode('hex'))
else:
return None
def _accomodate(self, size):
matches = glob.glob('%s/*' % (self.root))
matches.sort(key = lambda p: os.path.getmtime(p), reverse = True)
total_size = 0
for p in matches:
total_size += os.path.getsize(p)
if total_size + size > self.max_size:
os.remove(p)
def put(self, url, key, size):
path = self._url_to_path(url)
pattern = '%s-*' % (path)
matches = glob.glob(pattern)
for p in matches:
try:
os.remove(p)
except Exception as e:
log(str(e))
self._accomodate(size)
return '%s-%s' % (path, key.encode('hex'))
def download(url, cb = None, cb_count = 10):
c = Cache('~/.dcache', 10**9 * 4)
hit = c.get(url)
headers = {}
if hit:
headers = {'If-None-Match' : '%s' % (hit[1])}
req = urllib2.Request(url, None, headers)
try:
response = urllib2.urlopen(req)
if response.code == 200:
size = int(response.headers.get('Content-Length', 0))
key = response.headers.get('ETag', '')
path = c.put(url, key, size)
tmp = path + '_tmp'
with open(tmp, 'wb') as f:
buf = response.read(1024 * 1024)
n = 0
cb_i = 0
while buf:
n += len(buf)
rate = n / float(size)
if cb is not None:
if cb_i < int(rate * cb_count):
cb_i = int(rate * cb_count)
cb(n, size)
f.write(buf)
buf = response.read(1024 * 1024)
os.rename(tmp, path)
return path
else:
return None
except urllib2.HTTPError as e:
if e.code == 304:
return hit and hit[0]
else:
return None