summaryrefslogtreecommitdiff
path: root/wsgitools/scgi/__init__.py
diff options
context:
space:
mode:
authorHelmut Grohne <helmut@subdivi.de>2012-03-17 22:14:47 +0100
committerHelmut Grohne <helmut@subdivi.de>2012-03-17 22:14:47 +0100
commit863e29c4d2ed76ddf35a0bc3b66abe7423125362 (patch)
treee3c5008f31574a5e84b159b660f38e17d53dfa1e /wsgitools/scgi/__init__.py
parent31aef51e3badfd2fdafdc6c15b3a58fcb04607fd (diff)
downloadwsgitools-863e29c4d2ed76ddf35a0bc3b66abe7423125362.tar.gz
sendfile support
When a sendfile library is available, expose it via wsgi.file_wrapper. This support spans both asynchronous and forkpool.
Diffstat (limited to 'wsgitools/scgi/__init__.py')
-rw-r--r--wsgitools/scgi/__init__.py57
1 files changed, 57 insertions, 0 deletions
diff --git a/wsgitools/scgi/__init__.py b/wsgitools/scgi/__init__.py
index 20f6625..cbe7a80 100644
--- a/wsgitools/scgi/__init__.py
+++ b/wsgitools/scgi/__init__.py
@@ -1,3 +1,58 @@
+__all__ = []
+
+try:
+ import sendfile
+except ImportError:
+ have_sendfile = False
+else:
+ have_sendfile = True
+
+class FileWrapper:
+ """
+ @ivar offset: Initially 0. Becomes -1 when reading using next and
+ becomes positive when reading using next. In the latter case it
+ counts the number of bytes sent. It also ensures that next and
+ transfer are never mixed.
+ """
+ def __init__(self, filelike, blksize=8192):
+ self.filelike = filelike
+ self.blksize = blksize
+ self.offset = 0
+ if hasattr(filelike, "close"):
+ self.close = filelike.close
+
+ def can_transfer(self):
+ return have_sendfile and hasattr(self.filelike, "fileno") and \
+ self.offset >= 0
+
+ def transfer(self, sock, blksize=None):
+ assert self.offset >= 0
+ if blksize is None:
+ blksize = self.blksize
+ else:
+ blksize = min(self.blksize, blksize)
+ try:
+ sent = sendfile.sendfile(sock.fileno(), self.filelike.fileno(),
+ self.offset, blksize)
+ except OSError:
+ return -1
+ # There are two different sendfile libraries. Yeah!
+ if isinstance(sent, tuple):
+ sent = sent[1]
+ self.offset += sent
+ return sent
+
+ def __iter__(self):
+ return self
+
+ def next(self):
+ assert self.offset <= 0
+ self.offset = -1
+ data = self.filelike.read(self.blksize)
+ if data:
+ return data
+ raise StopIteration
+
def _convert_environ(environ, multithread=False, multiprocess=False,
run_once=False):
environ.update({
@@ -13,3 +68,5 @@ def _convert_environ(environ, multithread=False, multiprocess=False,
except KeyError:
pass
environ.pop("HTTP_CONTENT_LENGTH", None) # TODO: better way?
+ if have_sendfile:
+ environ["wsgi.file_wrapper"] = FileWrapper