summaryrefslogtreecommitdiff
path: root/wsgitools
diff options
context:
space:
mode:
authorHelmut Grohne <helmut@subdivi.de>2008-09-22 19:06:38 +0200
committerHelmut Grohne <helmut@subdivi.de>2008-09-22 19:06:38 +0200
commit0f08962bd848ce9a0fbf71684fc72fd0776da84d (patch)
tree70706313e681728bcfce4660e1402484e1a88b6f /wsgitools
parent091045c7049f7f851bb8166cac5ecbbf828ca6e2 (diff)
downloadwsgitools-0f08962bd848ce9a0fbf71684fc72fd0776da84d.tar.gz
added applications.StaticFile
Diffstat (limited to 'wsgitools')
-rw-r--r--wsgitools/applications.py90
1 files changed, 90 insertions, 0 deletions
diff --git a/wsgitools/applications.py b/wsgitools/applications.py
index 9112e45..909500e 100644
--- a/wsgitools/applications.py
+++ b/wsgitools/applications.py
@@ -1,3 +1,8 @@
+import os.path
+
+__all__ = []
+
+__all__.append("StaticContent")
class StaticContent:
"""
This wsgi application provides static content on whatever request it
@@ -43,3 +48,88 @@ class StaticContent:
if environ["REQUEST_METHOD"].upper() == "HEAD":
return []
return self.content
+
+__all__.append("StaticFile")
+class StaticFile:
+ """
+ This wsgi application provides the content of a static file on whatever
+ request it receives with method GET or HEAD (content stripped). If not
+ present, a content-length header is computed.
+ """
+ def __init__(self, filelike, status="200 OK", headers=list(),
+ blocksize=4096):
+ """
+ @type status: str
+ @param status: is the HTTP status returned to the browser
+ @type headers: [(str, str)]
+ @param headers: is a list of (header, value) pairs being delivered as
+ HTTP headers
+ @type filelike: str or file-like
+ @param filelike: may either be an path in the local file system or a
+ file-like that must support read(size) and seek(0). If tell()
+ is present, seek(0, 2) and tell() will be used to compute the
+ content-length.
+ @type blocksize: int
+ @param blocksize: the content is provided in chunks of this size
+ """
+ self.filelike = filelike
+ self.status = status
+ self.headers = headers
+ self.blocksize = blocksize
+
+ def _serve_in_chunks(self, stream):
+ """internal method yielding data from the given stream"""
+ while True:
+ data = stream.read(self.blocksize)
+ if not data:
+ break
+ yield data
+ if isinstance(self.filelike, basestring):
+ stream.close()
+
+ def __call__(self, environ, start_response):
+ """wsgi interface"""
+ assert isinstance(environ, dict)
+
+ if environ["REQUEST_METHOD"].upper() not in ["GET", "HEAD"]:
+ resp = "Request method not implemented"
+ start_response("501 Not Implemented",
+ [("Content-length", str(len(resp)))])
+ return [resp]
+
+ stream = None
+ size = -1
+ try:
+ if isinstance(self.filelike, basestring):
+ # raises IOError
+ stream = file(self.filelike)
+ size = os.path.getsize(self.filelike)
+ else:
+ stream = self.filelike
+ if hasattr(stream, "tell"):
+ stream.seek(0, 2)
+ size = stream.tell()
+ stream.seek(0)
+ except IOError:
+ resp = "File not found"
+ start_response("404 File not found",
+ [("Content-length", str(len(resp)))])
+ return [resp]
+
+ headers = list(self.headers)
+ if size >= 0:
+ if not [v for h, v in headers if h.lower() == "content-length"]:
+ headers.append(("Content-length", str(size)))
+
+ start_response(self.status, headers)
+ if environ["REQUEST_METHOD"].upper() == "HEAD":
+ if isinstance(self.filelike, basestring):
+ stream.close()
+ return []
+
+ if 0 <= size <= self.blocksize:
+ data = stream.read(size)
+ if isinstance(self.filelike, basestring):
+ stream.close()
+ return [data]
+ return self._serve_in_chunks(stream)