diff --git a/examples/hello.txt b/examples/hello.txt new file mode 100644 index 00000000..cd087558 --- /dev/null +++ b/examples/hello.txt @@ -0,0 +1 @@ +Hello world! diff --git a/examples/sendfile.py b/examples/sendfile.py new file mode 100644 index 00000000..d599f366 --- /dev/null +++ b/examples/sendfile.py @@ -0,0 +1,23 @@ +# -*- coding: utf-8 - +# +# This file is part of gunicorn released under the MIT license. +# See the NOTICE for more information. +# +# Example code from Eventlet sources + +import os +from wsgiref.validate import validator + +#@validator +def app(environ, start_response): + """Simplest possible application object""" + status = '200 OK' + fname = os.path.join(os.path.dirname(__file__), "hello.txt") + f = open(fname, 'rb') + + response_headers = [ + ('Content-type','text/plain'), + ] + start_response(status, response_headers) + + return environ['wsgi.file_wrapper'](f) diff --git a/gunicorn/http/_sendfile.py b/gunicorn/http/_sendfile.py new file mode 100644 index 00000000..b3854824 --- /dev/null +++ b/gunicorn/http/_sendfile.py @@ -0,0 +1,66 @@ +# -*- coding: utf-8 - +# +# This file is part of gunicorn released under the MIT license. +# See the NOTICE for more information. + +import errno +import os +import sys + +try: + import ctypes + import ctypes.util +except MemoryError: + # selinux execmem denial + # https://bugzilla.redhat.com/show_bug.cgi?id=488396 + raise ImportError + +SUPPORTED_PLATFORMS = ( + 'darwin', + 'freebsd', + 'dragonfly' + 'linux2') + +if sys.version_info < (2, 6) or \ + sys.platform not in SUPPORTED_PLATFORMS: + raise ImportError("sendfile isn't supported on this platform") + +_libc = ctypes.CDLL(ctypes.util.find_library("c"), use_errno=True) +_sendfile = _libc.sendfile + +def sendfile(fdout, fdin, offset, nbytes): + if sys.platform == 'darwin': + _sendfile.argtypes = [ctypes.c_int, ctypes.c_int, ctypes.c_uint64, + ctypes.POINTER(ctypes.c_uint64), ctypes.c_voidp, + ctypes.c_int] + _nbytes = ctypes.c_uint64(nbytes) + result = _sendfile(fdin, fdout, offset, _nbytes, None, 0) + if result == -1: + e = ctypes.get_errno() + if e == errno.EAGAIN and _nbytes.value: + return nbytes.value + raise OSError(e, os.strerror(e)) + return _nbytes.value + elif sys.platform in ('freebsd', 'dragonfly',): + _sendfile.argtypes = [ctypes.c_int, ctypes.c_int, ctypes.c_uint64, + ctypes.c_uint64, ctypes.c_voidp, + ctypes.POINTER(ctypes.c_uint64), ctypes.c_int] + _sbytes = ctypes.c_uint64(nbytes) + result = _sendfile(fdin, fdout, offset, nbytes, None, _sbytes, 0) + if result == -1: + e = ctypes.get_errno() + if e == errno.EAGAIN and _sbytes.value: + return _sbytes.value + raise OSError(e, os.strerror(e)) + return _sbytes.value + + else: + _sendfile.argtypes = [ctypes.c_int, ctypes.c_int, + ctypes.POINTER(ctypes.c_uint64), ctypes.c_size_t] + + _offset = ctypes.c_uint64(offset) + sent = _sendfile(fdout, fdin, _offset, nbytes) + if sent == -1: + e = ctypess.get_errno() + raise OSError(e, os.strerror(e)) + return sent diff --git a/gunicorn/http/sendfile.py b/gunicorn/http/sendfile.py deleted file mode 100644 index f410b0c0..00000000 --- a/gunicorn/http/sendfile.py +++ /dev/null @@ -1,61 +0,0 @@ -# -*- coding: utf-8 - -# -# This file is part of gunicorn released under the MIT license. -# See the NOTICE for more information. - -import errno -import os -import sys - -# Python on Solaris compiled with Sun Studio doesn't have ctypes. -try: - import ctypes - import ctypes.util - - if sys.version_info >= (2, 6): - _libc = ctypes.CDLL(ctypes.util.find_library("c"), use_errno=True) - _sendfile = _libc.sendfile - else: - _sendfile = None - -except ImportError: - _sendfile = None - -if _sendfile: - if sys.platform == 'darwin': - # MacOS X - int sendfile(int fd, int s, off_t offset, off_t *len, - # struct sf_hdtr *hdtr, int flags); - - _sendfile.argtypes = [ctypes.c_int, ctypes.c_int, ctypes.c_uint64, - ctypes.POINTER(ctypes.c_uint64), ctypes.c_voidp, - ctypes.c_int] - - def sendfile(fileno, sockno, offset, nbytes): - _nbytes = ctypes.c_uint64(nbytes) - result = _sendfile(fileno, sockno, offset, _nbytes, None, 0) - if result == -1: - e = ctypes.get_errno() - if e == errno.EAGAIN and _nbytes.value: - return _nbytes.value - raise OSError(e, os.strerror(e)) - return _nbytes.value - - elif sys.platform == 'linux2': - # Linux - size_t sendfile(int out_fd, int in_fd, off_t *offset, - # size_t count); - - _sendfile.argtypes = [ctypes.c_int, ctypes.c_int, - ctypes.POINTER(ctypes.c_uint64), ctypes.c_size_t] - - def sendfile(fileno, sockno, offset, nbytes): - _offset = ctypes.c_uint64(offset) - result = _sendfile(sockno, fileno, _offset, nbytes) - if result == -1: - e = ctypes.get_errno() - raise OSError(e, os.strerror(e)) - return result - - else: - sendfile = None -else: - sendfile = None diff --git a/gunicorn/http/wsgi.py b/gunicorn/http/wsgi.py index 1574a629..150b7d1c 100644 --- a/gunicorn/http/wsgi.py +++ b/gunicorn/http/wsgi.py @@ -15,8 +15,11 @@ import gunicorn.util as util try: # Python 3.3 has os.sendfile(). from os import sendfile -except: - from sendfile import sendfile +except ImportError: + try: + from _senfile import sendfile + except ImportError: + sendfile = None NORMALIZE_SPACE = re.compile(r'(?:\r\n)?[ \t]+') @@ -271,8 +274,9 @@ class Response(object): sent += sendfile(fileno, sockno, offset+sent, nbytes-sent) def write_file(self, respiter): - if sendfile and hasattr(respiter.filelike, 'fileno') and \ - hasattr(respiter.filelike, 'tell'): + if sendfile is not None and \ + hasattr(respiter.filelike, 'fileno') and \ + hasattr(respiter.filelike, 'tell'): fileno = respiter.filelike.fileno() fd_offset = os.lseek(fileno, 0, os.SEEK_CUR)