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

Last change on this file 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
RevLine 
[1066]1import os
2import socket
3import sys
[1587]4from time import time
[1066]5from paste.script.appinstall import Installer as BaseInstaller
6from paste.fileapp import FileApp
[1082]7from paste import urlparser
8from paste import request
[1066]9from paste.httpexceptions import HTTPNotFound
[1603]10import urllib2
[1066]11
[3172]12FILES = ['ico','txt','tgz','.gz','egg','zip','exe','cfg']
[1603]13
[3172]14
[1066]15class PackageProxyApp(object):
16
[3172]17    def __init__(self, index_url=None, pack_dir=None, username=None,
18                 password=None, realm=None, stimeout=None, ptimeout=None):
[1066]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()
[1603]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)
[3172]36        if stimeout:
37            socket.setdefaulttimeout(float(stimeout))
[1066]38        self.index_url = index_url
39        self.pack_dir = pack_dir
[3172]40        self.ptimeout = ptimeout or 3600
[1066]41
42    def __call__(self, environ, start_response):
[1603]43        """ serve the static files """
[1066]44        path = environ.get('PATH_INFO', '').strip()
[1083]45        path_parts = path.split('/')
46        if len(path_parts) > 1 and path_parts[1] == "favicon.ico":
[1082]47            return HTTPNotFound()(environ, start_response)
[1603]48        filename = self.checkCache(path[1:])
[1066]49        if filename is None:
50            return HTTPNotFound()(environ, start_response)
51        return FileApp(filename)(environ, start_response)
52
[1603]53    def checkCache(self, path):
54        """check if we already have the file and download it if not"""   
[1066]55        pth = self.pack_dir + path
[3172]56        index = 0 
57        if not (path[-3:] in FILES): 
[1603]58            if not os.path.exists(pth):
[3172]59                os.makedirs(pth) # create dir if it is not there
[1603]60            # add index.html for supposedly folders
61            pth = pth + 'index.html'
62            index = 1
[1066]63        else:
[1603]64            pth1 = '/'.join(pth.split('/')[:-1])
[1813]65            if not os.path.exists(pth1):
[3172]66                os.makedirs(pth1) # create parent dir if it is not there
[1603]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
[3172]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     
[1066]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)
[1603]90    username = local_conf.get('username', None)
91    password = local_conf.get('password', None)
92    realm = local_conf.get('realm', None)
[3172]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)
[1082]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):
[1083]117                start_response('200 OK', [('Content-Type', 'text/html')])
[1082]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
[1083]146        names = [i for i in os.listdir(path) if not i.startswith('.')]
[1082]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
[1066]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)
[1260]182"""
Note: See TracBrowser for help on using the repository browser.