source: products/quintagroup.distrpoxy/trunk/quintagroup/distproxy/wsgi.py @ 3172

Last change on this file since 3172 was 3172, checked in by kroman0, 13 years ago

Fixed problems with index downtime

  • Property svn:eol-style set to native
File size: 7.2 KB
Line 
1import os
2import socket
3import sys
4from time import time
5from paste.script.appinstall import Installer as BaseInstaller
6from paste.fileapp import FileApp
7from paste import urlparser
8from paste import request
9from paste.httpexceptions import HTTPNotFound
10import urllib2
11
12FILES = ['ico','txt','tgz','.gz','egg','zip','exe','cfg']
13
14
15class PackageProxyApp(object):
16
17    def __init__(self, index_url=None, pack_dir=None, username=None,
18                 password=None, realm=None, stimeout=None, ptimeout=None):
19        if not index_url: 
20            print "No repository index provided"
21            sys.exit()
22        if not pack_dir:
23            print "No packages cache directory provided"
24            sys.exit()
25        if not os.path.isdir(pack_dir):
26            print 'You must create the %r directory' % pack_dir
27            sys.exit()
28        if username and password:
29            # authenticate
30            password_mgr = urllib2.HTTPPasswordMgrWithDefaultRealm()
31            top_level_url = index_url
32            password_mgr.add_password(realm, top_level_url, username, password)
33            handler = urllib2.HTTPBasicAuthHandler(password_mgr)
34            opener = urllib2.build_opener(handler)
35            urllib2.install_opener(opener)
36        if stimeout:
37            socket.setdefaulttimeout(float(stimeout))
38        self.index_url = index_url
39        self.pack_dir = pack_dir
40        self.ptimeout = ptimeout or 3600
41
42    def __call__(self, environ, start_response):
43        """ serve the static files """
44        path = environ.get('PATH_INFO', '').strip()
45        path_parts = path.split('/')
46        if len(path_parts) > 1 and path_parts[1] == "favicon.ico":
47            return HTTPNotFound()(environ, start_response)
48        filename = self.checkCache(path[1:])
49        if filename is None:
50            return HTTPNotFound()(environ, start_response)
51        return FileApp(filename)(environ, start_response)
52
53    def checkCache(self, path):
54        """check if we already have the file and download it if not"""   
55        pth = self.pack_dir + path
56        index = 0 
57        if not (path[-3:] in FILES): 
58            if not os.path.exists(pth):
59                os.makedirs(pth) # create dir if it is not there
60            # add index.html for supposedly folders
61            pth = pth + 'index.html'
62            index = 1
63        else:
64            pth1 = '/'.join(pth.split('/')[:-1])
65            if not os.path.exists(pth1):
66                os.makedirs(pth1) # create parent dir if it is not there
67        url = self.index_url+path
68        #if we dont have download it
69        if not os.path.exists(pth):
70            f = urllib2.urlopen(url)
71            lf = open(pth,'wb')
72            lf.write(f.read())
73            lf.close()
74        #if we have the index.html file if it is older the 1 hour update
75        elif index and int(time()) - os.path.getmtime(pth) > self.ptimeout:
76            try:
77                f = urllib2.urlopen(url)
78                lf = open(pth,'wb')
79                lf.write(f.read())
80                lf.close()
81            except urllib2.URLError:
82                pass     
83        return pth
84
85def app_factory(global_config, **local_conf):
86    # Grab config from wsgi .ini file. If not specified, config.py's values
87    # take over.
88    pack_dir = local_conf.get('pack_directory', None)
89    index_url = local_conf.get('index', None)
90    username = local_conf.get('username', None)
91    password = local_conf.get('password', None)
92    realm = local_conf.get('realm', None)
93    stimeout = local_conf.get('stimeout', None)
94    ptimeout = local_conf.get('ptimeout', None)
95    return PackageProxyApp(index_url, pack_dir, username, password, realm, stimeout, ptimeout)
96
97class StaticURLParser(urlparser.StaticURLParser):
98
99    def __call__(self, environ, start_response):
100        path_info = environ.get('PATH_INFO', '')
101        if not path_info:
102            return self.add_slash(environ, start_response)
103        if path_info == '/':
104            # @@: This should obviously be configurable
105            filename = 'index.html'
106        else:
107            filename = request.path_info_pop(environ)
108        full = os.path.normcase(os.path.normpath(
109            os.path.join(self.directory, filename)))
110        if os.path.sep != '/':
111            full = full.replace('/', os.path.sep)
112        if self.root_directory is not None and not full.startswith(self.root_directory):
113            # Out of bounds
114            return self.not_found(environ, start_response)
115        if not os.path.exists(full):
116            if full.endswith('index.html') and not os.path.isfile(full):
117                start_response('200 OK', [('Content-Type', 'text/html')])
118                return [self.get_index_html()]
119            return self.not_found(environ, start_response)
120        if os.path.isdir(full):
121            # @@: Cache?
122            child_root = self.root_directory is not None and \
123                self.root_directory or self.directory
124            return self.__class__(full, root_directory=child_root,
125                                  cache_max_age=self.cache_max_age)(environ,
126                                                                   start_response)
127        if environ.get('PATH_INFO') and environ.get('PATH_INFO') != '/':
128            return self.error_extra_path(environ, start_response)
129        if_none_match = environ.get('HTTP_IF_NONE_MATCH')
130        if if_none_match:
131            mytime = os.stat(full).st_mtime
132            if str(mytime) == if_none_match:
133                headers = []
134                ETAG.update(headers, mytime)
135                start_response('304 Not Modified', headers)
136                return [''] # empty body
137
138        fa = self.make_app(full)
139        if self.cache_max_age:
140            fa.cache_control(max_age=self.cache_max_age)
141        return fa(environ, start_response)
142
143    def get_index_html(self):
144        path = self.directory
145        # create sorted lists of directories and files
146        names = [i for i in os.listdir(path) if not i.startswith('.')]
147        dirs = [i for i in names if os.path.isdir(os.path.join(path, i))]
148        dirs.sort()
149        files = [i for i in names if os.path.isfile(os.path.join(path, i))]
150        files.sort()
151        names = dirs + files
152        links = '\n'.join(['<li><a href="%s">%s</a></li>' %  (i, i) for i in names])
153        template = open(os.path.join(os.path.dirname(__file__), 'index.html')).read()
154        return template % {'path': path[len(self.root_directory):], 'links': links}
155
156def make_static(global_conf, document_root, cache_max_age=None):
157    """
158    Return a WSGI application that serves a directory (configured
159    with document_root)
160
161    cache_max_age - integer specifies CACHE_CONTROL max_age in seconds
162    """
163    if cache_max_age is not None:
164        cache_max_age = int(cache_max_age)
165    return StaticURLParser(
166        document_root, cache_max_age=cache_max_age)
167
168
169"""
170class Installer(BaseInstaller):
171    use_cheetah = False
172    config_file = 'deployment.ini_tmpl'
173
174    def config_content(self, command, vars):
175        import pkg_resources
176        module = 'collective.eggproxy'
177        if pkg_resources.resource_exists(module, self.config_file):
178            return self.template_renderer(
179                pkg_resources.resource_string(module, self.config_file),
180                vars,
181                filename=self.config_file)
182"""
Note: See TracBrowser for help on using the repository browser.