From 0f08962bd848ce9a0fbf71684fc72fd0776da84d Mon Sep 17 00:00:00 2001 From: Helmut Grohne Date: Mon, 22 Sep 2008 19:06:38 +0200 Subject: added applications.StaticFile --- wsgitools/applications.py | 90 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 90 insertions(+) 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) -- cgit v1.2.3