1 | import os |
---|
2 | import socket |
---|
3 | import sys |
---|
4 | from time import time |
---|
5 | from paste.script.appinstall import Installer as BaseInstaller |
---|
6 | from paste.fileapp import FileApp |
---|
7 | from paste import urlparser |
---|
8 | from paste import request |
---|
9 | from paste.httpexceptions import HTTPNotFound |
---|
10 | from spider import webmirror |
---|
11 | |
---|
12 | class PackageProxyApp(object): |
---|
13 | |
---|
14 | def __init__(self, index_url=None, pack_dir=None): |
---|
15 | if not index_url: |
---|
16 | print "No repository index provided" |
---|
17 | sys.exit() |
---|
18 | if not pack_dir: |
---|
19 | print "No packages cache directory provided" |
---|
20 | sys.exit() |
---|
21 | if not os.path.isdir(pack_dir): |
---|
22 | print 'You must create the %r directory' % pack_dir |
---|
23 | sys.exit() |
---|
24 | self.index_url = index_url |
---|
25 | self.pack_dir = pack_dir |
---|
26 | |
---|
27 | def __call__(self, environ, start_response): |
---|
28 | path = environ.get('PATH_INFO', '').strip() |
---|
29 | path_parts = path.split('/') |
---|
30 | if len(path_parts) > 1 and path_parts[1] == "favicon.ico": |
---|
31 | return HTTPNotFound()(environ, start_response) |
---|
32 | filename = self.checkCache(path) |
---|
33 | if filename is None: |
---|
34 | return HTTPNotFound()(environ, start_response) |
---|
35 | return FileApp(filename)(environ, start_response) |
---|
36 | |
---|
37 | def checkCache(self, path): |
---|
38 | pth = self.pack_dir + path |
---|
39 | pth1 = pth |
---|
40 | if not (path[-3:] in ['tgz','.gz','egg','zip','exe','cfg']): |
---|
41 | pth1 = pth + 'index.html' |
---|
42 | else: |
---|
43 | pth = '/'.join(pth.split('/')[:-1]) |
---|
44 | if not os.path.exists(pth1): |
---|
45 | webmirror(pth,1,self.index_url+path,0,1) |
---|
46 | elif int(time()) - os.path.getmtime(pth1) > 3600: |
---|
47 | webmirror(pth,1,self.index_url+path,0,1) |
---|
48 | if pth1: |
---|
49 | return pth1 |
---|
50 | return pth |
---|
51 | |
---|
52 | def app_factory(global_config, **local_conf): |
---|
53 | # Grab config from wsgi .ini file. If not specified, config.py's values |
---|
54 | # take over. |
---|
55 | pack_dir = local_conf.get('pack_directory', None) |
---|
56 | index_url = local_conf.get('index', None) |
---|
57 | return PackageProxyApp(index_url, pack_dir) |
---|
58 | |
---|
59 | class StaticURLParser(urlparser.StaticURLParser): |
---|
60 | |
---|
61 | def __call__(self, environ, start_response): |
---|
62 | path_info = environ.get('PATH_INFO', '') |
---|
63 | if not path_info: |
---|
64 | return self.add_slash(environ, start_response) |
---|
65 | if path_info == '/': |
---|
66 | # @@: This should obviously be configurable |
---|
67 | filename = 'index.html' |
---|
68 | else: |
---|
69 | filename = request.path_info_pop(environ) |
---|
70 | full = os.path.normcase(os.path.normpath( |
---|
71 | os.path.join(self.directory, filename))) |
---|
72 | if os.path.sep != '/': |
---|
73 | full = full.replace('/', os.path.sep) |
---|
74 | if self.root_directory is not None and not full.startswith(self.root_directory): |
---|
75 | # Out of bounds |
---|
76 | return self.not_found(environ, start_response) |
---|
77 | if not os.path.exists(full): |
---|
78 | if full.endswith('index.html') and not os.path.isfile(full): |
---|
79 | start_response('200 OK', [('Content-Type', 'text/html')]) |
---|
80 | return [self.get_index_html()] |
---|
81 | return self.not_found(environ, start_response) |
---|
82 | if os.path.isdir(full): |
---|
83 | # @@: Cache? |
---|
84 | child_root = self.root_directory is not None and \ |
---|
85 | self.root_directory or self.directory |
---|
86 | return self.__class__(full, root_directory=child_root, |
---|
87 | cache_max_age=self.cache_max_age)(environ, |
---|
88 | start_response) |
---|
89 | if environ.get('PATH_INFO') and environ.get('PATH_INFO') != '/': |
---|
90 | return self.error_extra_path(environ, start_response) |
---|
91 | if_none_match = environ.get('HTTP_IF_NONE_MATCH') |
---|
92 | if if_none_match: |
---|
93 | mytime = os.stat(full).st_mtime |
---|
94 | if str(mytime) == if_none_match: |
---|
95 | headers = [] |
---|
96 | ETAG.update(headers, mytime) |
---|
97 | start_response('304 Not Modified', headers) |
---|
98 | return [''] # empty body |
---|
99 | |
---|
100 | fa = self.make_app(full) |
---|
101 | if self.cache_max_age: |
---|
102 | fa.cache_control(max_age=self.cache_max_age) |
---|
103 | return fa(environ, start_response) |
---|
104 | |
---|
105 | def get_index_html(self): |
---|
106 | path = self.directory |
---|
107 | # create sorted lists of directories and files |
---|
108 | names = [i for i in os.listdir(path) if not i.startswith('.')] |
---|
109 | dirs = [i for i in names if os.path.isdir(os.path.join(path, i))] |
---|
110 | dirs.sort() |
---|
111 | files = [i for i in names if os.path.isfile(os.path.join(path, i))] |
---|
112 | files.sort() |
---|
113 | names = dirs + files |
---|
114 | links = '\n'.join(['<li><a href="%s">%s</a></li>' % (i, i) for i in names]) |
---|
115 | template = open(os.path.join(os.path.dirname(__file__), 'index.html')).read() |
---|
116 | return template % {'path': path[len(self.root_directory):], 'links': links} |
---|
117 | |
---|
118 | def make_static(global_conf, document_root, cache_max_age=None): |
---|
119 | """ |
---|
120 | Return a WSGI application that serves a directory (configured |
---|
121 | with document_root) |
---|
122 | |
---|
123 | cache_max_age - integer specifies CACHE_CONTROL max_age in seconds |
---|
124 | """ |
---|
125 | if cache_max_age is not None: |
---|
126 | cache_max_age = int(cache_max_age) |
---|
127 | return StaticURLParser( |
---|
128 | document_root, cache_max_age=cache_max_age) |
---|
129 | |
---|
130 | |
---|
131 | """ |
---|
132 | class Installer(BaseInstaller): |
---|
133 | use_cheetah = False |
---|
134 | config_file = 'deployment.ini_tmpl' |
---|
135 | |
---|
136 | def config_content(self, command, vars): |
---|
137 | import pkg_resources |
---|
138 | module = 'collective.eggproxy' |
---|
139 | if pkg_resources.resource_exists(module, self.config_file): |
---|
140 | return self.template_renderer( |
---|
141 | pkg_resources.resource_string(module, self.config_file), |
---|
142 | vars, |
---|
143 | filename=self.config_file) |
---|
144 | """ |
---|