summaryrefslogtreecommitdiff
path: root/paste/fileapp.py
diff options
context:
space:
mode:
authorcce <devnull@localhost>2007-08-07 19:55:38 +0000
committercce <devnull@localhost>2007-08-07 19:55:38 +0000
commitede8e9e3e0e5d4cb9cdf9c900a20284e3d1e2bad (patch)
treec0ae6d9505989932692f5192f885524431a768e8 /paste/fileapp.py
parent545a9ab4eec16fc818526b0d5be72a3bf32d1763 (diff)
downloadpaste-ede8e9e3e0e5d4cb9cdf9c900a20284e3d1e2bad.tar.gz
first pass at an ArchiveStore
Diffstat (limited to 'paste/fileapp.py')
-rw-r--r--paste/fileapp.py61
1 files changed, 58 insertions, 3 deletions
diff --git a/paste/fileapp.py b/paste/fileapp.py
index e9ba553..e72db7f 100644
--- a/paste/fileapp.py
+++ b/paste/fileapp.py
@@ -9,19 +9,19 @@ files. At this time it has cache helpers and understands the
if-modified-since request header.
"""
-import os, time, mimetypes
+import os, time, mimetypes, zipfile, tarfile
from paste.httpexceptions import *
from paste.httpheaders import *
CACHE_SIZE = 4096
BLOCK_SIZE = 4096 * 16
-__all__ = ['DataApp', 'FileApp']
+__all__ = ['DataApp', 'FileApp', 'ArchiveStore']
class DataApp(object):
"""
Returns an application that will send content in a single chunk,
- this application has support for setting cashe-control and for
+ this application has support for setting cache-control and for
responding to conditional (or HEAD) requests.
Constructor Arguments:
@@ -256,3 +256,58 @@ class _FileIter(object):
def close(self):
self.file.close()
+class ArchiveStore(object):
+ """
+ Returns an application that serves up a DataApp for items requested
+ in a given zip or tar archive.
+
+ Constructor Arguments:
+
+ ``filepath`` the path to the archive being served
+
+ ``cache_control()``
+
+ This method provides validated construction of the ``Cache-Control``
+ header as well as providing for automated filling out of the
+ ``EXPIRES`` header for HTTP/1.0 clients.
+ """
+
+ def __init__(self, filepath):
+ if zipfile.is_zipfile(filepath):
+ self.archive = zipfile.ZipFile(filepath,"r")
+ elif tarfile.is_tarfile(filepath):
+ self.archive = tarfile.TarFileCompat(filepath,"r")
+ else:
+ raise AssertionError("filepath '%s' is not a zip or tar " % filepath)
+ self.expires = None
+ self.last_modified = time.time()
+ self.cache = {}
+
+ def cache_control(self, **kwargs):
+ self.expires = CACHE_CONTROL.apply(self.headers, **kwargs) or None
+ return self
+
+ def __call__(self, environ, start_response):
+ path = environ.get("PATH_INFO","")
+ if path.startswith("/"):
+ path = path[1:]
+ application = self.cache.get(path)
+ if application:
+ return application(environ, start_response)
+ try:
+ info = self.archive.getinfo(path)
+ except KeyError:
+ exc = HTTPNotFound("The file requested, '%s', was not found." % path)
+ return exc.wsgi_application(environ, start_response)
+ if info.filename.endswith("/"):
+ exc = HTTPNotFound("Path requested, '%s', is not a file." % path)
+ return exc.wsgi_application(environ, start_response)
+ content_type, content_encoding = mimetypes.guess_type(info.filename)
+ app = DataApp(None, content_type = content_type,
+ content_encoding = content_encoding)
+ app.set_content(self.archive.read(path),
+ time.mktime(info.date_time + (0,0,0)))
+ self.cache[path] = app
+ app.expires = self.expires
+ return app(environ, start_response)
+