mirror of
https://github.com/frappe/gunicorn.git
synced 2026-01-14 11:09:11 +08:00
Merge pull request #1094 from benoitc/1038-sendfile-seek
Simplify sendfile logic
This commit is contained in:
commit
6b92575e00
@ -12,7 +12,6 @@ import sys
|
|||||||
from gunicorn._compat import unquote_to_wsgi_str
|
from gunicorn._compat import unquote_to_wsgi_str
|
||||||
from gunicorn.six import string_types, binary_type, reraise
|
from gunicorn.six import string_types, binary_type, reraise
|
||||||
from gunicorn import SERVER_SOFTWARE
|
from gunicorn import SERVER_SOFTWARE
|
||||||
import gunicorn.six as six
|
|
||||||
import gunicorn.util as util
|
import gunicorn.util as util
|
||||||
|
|
||||||
try:
|
try:
|
||||||
@ -24,6 +23,10 @@ except ImportError:
|
|||||||
except ImportError:
|
except ImportError:
|
||||||
sendfile = None
|
sendfile = None
|
||||||
|
|
||||||
|
# Send files in at most 1GB blocks as some operating systems can have problems
|
||||||
|
# with sending files in blocks over 2GB.
|
||||||
|
BLKSIZE = 0x3FFFFFFF
|
||||||
|
|
||||||
NORMALIZE_SPACE = re.compile(r'(?:\r\n)?[ \t]+')
|
NORMALIZE_SPACE = re.compile(r'(?:\r\n)?[ \t]+')
|
||||||
|
|
||||||
log = logging.getLogger(__name__)
|
log = logging.getLogger(__name__)
|
||||||
@ -344,77 +347,51 @@ class Response(object):
|
|||||||
util.write(self.sock, arg, self.chunked)
|
util.write(self.sock, arg, self.chunked)
|
||||||
|
|
||||||
def can_sendfile(self):
|
def can_sendfile(self):
|
||||||
return (self.cfg.sendfile and (sendfile is not None))
|
return self.cfg.sendfile and sendfile is not None
|
||||||
|
|
||||||
def sendfile_all(self, fileno, sockno, offset, nbytes):
|
def sendfile(self, respiter):
|
||||||
# Send file in at most 1GB blocks as some operating
|
if self.cfg.is_ssl or not self.can_sendfile():
|
||||||
# systems can have problems with sending files in blocks
|
return False
|
||||||
# over 2GB.
|
|
||||||
|
|
||||||
BLKSIZE = 0x3FFFFFFF
|
try:
|
||||||
|
fileno = respiter.filelike.fileno()
|
||||||
|
offset = os.lseek(fileno, 0, os.SEEK_CUR)
|
||||||
|
if self.response_length is None:
|
||||||
|
filesize = os.fstat(fileno).st_size
|
||||||
|
|
||||||
if nbytes > BLKSIZE:
|
# The file may be special and sendfile will fail.
|
||||||
for m in range(0, nbytes, BLKSIZE):
|
# It may also be zero-length, but that is okay.
|
||||||
self.sendfile_all(fileno, sockno, offset, min(nbytes, BLKSIZE))
|
if filesize == 0:
|
||||||
offset += BLKSIZE
|
return False
|
||||||
nbytes -= BLKSIZE
|
|
||||||
else:
|
|
||||||
sent = 0
|
|
||||||
sent += sendfile(sockno, fileno, offset + sent, nbytes - sent)
|
|
||||||
while sent != nbytes:
|
|
||||||
sent += sendfile(sockno, fileno, offset + sent, nbytes - sent)
|
|
||||||
|
|
||||||
def sendfile_use_send(self, fileno, fo_offset, nbytes):
|
nbytes = filesize - offset
|
||||||
|
else:
|
||||||
|
nbytes = self.response_length
|
||||||
|
except (OSError, io.UnsupportedOperation):
|
||||||
|
return False
|
||||||
|
|
||||||
# send file in blocks of 8182 bytes
|
self.send_headers()
|
||||||
BLKSIZE = 8192
|
|
||||||
|
|
||||||
|
if self.is_chunked():
|
||||||
|
chunk_size = "%X\r\n" % nbytes
|
||||||
|
self.sock.sendall(chunk_size.encode('utf-8'))
|
||||||
|
|
||||||
|
sockno = self.sock.fileno()
|
||||||
sent = 0
|
sent = 0
|
||||||
while sent != nbytes:
|
|
||||||
data = os.read(fileno, BLKSIZE)
|
|
||||||
if not data:
|
|
||||||
break
|
|
||||||
|
|
||||||
sent += len(data)
|
for m in range(0, nbytes, BLKSIZE):
|
||||||
if sent > nbytes:
|
count = min(nbytes - sent, BLKSIZE)
|
||||||
data = data[:nbytes - sent]
|
sent += sendfile(sockno, fileno, offset + sent, count)
|
||||||
|
|
||||||
util.write(self.sock, data, self.chunked)
|
if self.is_chunked():
|
||||||
|
self.sock.sendall(b"\r\n")
|
||||||
|
|
||||||
|
os.lseek(fileno, offset, os.SEEK_SET)
|
||||||
|
|
||||||
|
return True
|
||||||
|
|
||||||
def write_file(self, respiter):
|
def write_file(self, respiter):
|
||||||
if self.can_sendfile() and util.is_fileobject(respiter.filelike):
|
if not self.sendfile(respiter):
|
||||||
# sometimes the fileno isn't a callable
|
|
||||||
if six.callable(respiter.filelike.fileno):
|
|
||||||
fileno = respiter.filelike.fileno()
|
|
||||||
else:
|
|
||||||
fileno = respiter.filelike.fileno
|
|
||||||
|
|
||||||
fd_offset = os.lseek(fileno, 0, os.SEEK_CUR)
|
|
||||||
fo_offset = respiter.filelike.tell()
|
|
||||||
nbytes = max(os.fstat(fileno).st_size - fo_offset, 0)
|
|
||||||
|
|
||||||
if self.response_length:
|
|
||||||
nbytes = min(nbytes, self.response_length)
|
|
||||||
|
|
||||||
if nbytes == 0:
|
|
||||||
return
|
|
||||||
|
|
||||||
self.send_headers()
|
|
||||||
|
|
||||||
if self.cfg.is_ssl:
|
|
||||||
self.sendfile_use_send(fileno, fo_offset, nbytes)
|
|
||||||
else:
|
|
||||||
if self.is_chunked():
|
|
||||||
chunk_size = "%X\r\n" % nbytes
|
|
||||||
self.sock.sendall(chunk_size.encode('utf-8'))
|
|
||||||
|
|
||||||
self.sendfile_all(fileno, self.sock.fileno(), fo_offset, nbytes)
|
|
||||||
|
|
||||||
if self.is_chunked():
|
|
||||||
self.sock.sendall(b"\r\n")
|
|
||||||
|
|
||||||
os.lseek(fileno, fd_offset, os.SEEK_SET)
|
|
||||||
else:
|
|
||||||
for item in respiter:
|
for item in respiter:
|
||||||
self.write(item)
|
self.write(item)
|
||||||
|
|
||||||
|
|||||||
@ -7,7 +7,6 @@ from __future__ import print_function
|
|||||||
|
|
||||||
import email.utils
|
import email.utils
|
||||||
import fcntl
|
import fcntl
|
||||||
import io
|
|
||||||
import os
|
import os
|
||||||
import pkg_resources
|
import pkg_resources
|
||||||
import random
|
import random
|
||||||
@ -517,19 +516,6 @@ def to_latin1(value):
|
|||||||
return value.encode("latin-1")
|
return value.encode("latin-1")
|
||||||
|
|
||||||
|
|
||||||
def is_fileobject(obj):
|
|
||||||
if not hasattr(obj, "tell") or not hasattr(obj, "fileno"):
|
|
||||||
return False
|
|
||||||
|
|
||||||
# check BytesIO case and maybe others
|
|
||||||
try:
|
|
||||||
obj.fileno()
|
|
||||||
except (IOError, io.UnsupportedOperation):
|
|
||||||
return False
|
|
||||||
|
|
||||||
return True
|
|
||||||
|
|
||||||
|
|
||||||
def warn(msg):
|
def warn(msg):
|
||||||
print("!!!", file=sys.stderr)
|
print("!!!", file=sys.stderr)
|
||||||
|
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user