From 863e29c4d2ed76ddf35a0bc3b66abe7423125362 Mon Sep 17 00:00:00 2001 From: Helmut Grohne Date: Sat, 17 Mar 2012 22:14:47 +0100 Subject: sendfile support When a sendfile library is available, expose it via wsgi.file_wrapper. This support spans both asynchronous and forkpool. --- wsgitools/scgi/__init__.py | 57 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 57 insertions(+) (limited to 'wsgitools/scgi/__init__.py') 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 -- cgit v1.2.3