diff options
author | Helmut Grohne <helmut@subdivi.de> | 2012-03-17 22:14:47 +0100 |
---|---|---|
committer | Helmut Grohne <helmut@subdivi.de> | 2012-03-17 22:14:47 +0100 |
commit | 863e29c4d2ed76ddf35a0bc3b66abe7423125362 (patch) | |
tree | e3c5008f31574a5e84b159b660f38e17d53dfa1e /wsgitools/scgi/__init__.py | |
parent | 31aef51e3badfd2fdafdc6c15b3a58fcb04607fd (diff) | |
download | wsgitools-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__.py | 57 |
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 |