mirror of
https://github.com/kovidgoyal/calibre.git
synced 2025-08-11 09:13:57 -04:00
Remove ctypes based sendfile wrapper since os.sendfile is available in py3
This commit is contained in:
parent
2b08beb226
commit
f3e5461d00
@ -5,28 +5,33 @@
|
|||||||
__license__ = 'GPL v3'
|
__license__ = 'GPL v3'
|
||||||
__copyright__ = '2015, Kovid Goyal <kovid at kovidgoyal.net>'
|
__copyright__ = '2015, Kovid Goyal <kovid at kovidgoyal.net>'
|
||||||
|
|
||||||
import os, hashlib, uuid, struct
|
import errno
|
||||||
|
import hashlib
|
||||||
|
import os
|
||||||
|
import struct
|
||||||
|
import uuid
|
||||||
from collections import namedtuple
|
from collections import namedtuple
|
||||||
from io import BytesIO, DEFAULT_BUFFER_SIZE
|
from functools import wraps
|
||||||
|
from io import DEFAULT_BUFFER_SIZE, BytesIO
|
||||||
from itertools import chain, repeat
|
from itertools import chain, repeat
|
||||||
from operator import itemgetter
|
from operator import itemgetter
|
||||||
from functools import wraps
|
|
||||||
|
|
||||||
from polyglot.builtins import iteritems, itervalues, reraise, map, unicode_type, string_or_bytes
|
from calibre import force_unicode, guess_type
|
||||||
|
|
||||||
from calibre import guess_type, force_unicode
|
|
||||||
from calibre.constants import __version__
|
from calibre.constants import __version__
|
||||||
from calibre.srv.loop import WRITE
|
|
||||||
from calibre.srv.errors import HTTPSimpleResponse
|
from calibre.srv.errors import HTTPSimpleResponse
|
||||||
from calibre.srv.http_request import HTTPRequest, read_headers
|
from calibre.srv.http_request import HTTPRequest, read_headers
|
||||||
from calibre.srv.sendfile import file_metadata, sendfile_to_socket_async, CannotSendfile, SendfileInterrupted
|
from calibre.srv.loop import WRITE
|
||||||
from calibre.srv.utils import (
|
from calibre.srv.utils import (
|
||||||
MultiDict, http_date, HTTP1, HTTP11, socket_errors_socket_closed,
|
HTTP1, HTTP11, Cookie, MultiDict, fast_now_strftime, get_translator_for_lang,
|
||||||
sort_q_values, get_translator_for_lang, Cookie, fast_now_strftime)
|
http_date, socket_errors_socket_closed, sort_q_values
|
||||||
from calibre.utils.speedups import ReadOnlyFileBuffer
|
)
|
||||||
from calibre.utils.monotonic import monotonic
|
from calibre.utils.monotonic import monotonic
|
||||||
|
from calibre.utils.speedups import ReadOnlyFileBuffer
|
||||||
from polyglot import http_client, reprlib
|
from polyglot import http_client, reprlib
|
||||||
from polyglot.builtins import error_message
|
from polyglot.builtins import (
|
||||||
|
error_message, iteritems, itervalues, map, reraise, string_or_bytes,
|
||||||
|
unicode_type
|
||||||
|
)
|
||||||
|
|
||||||
Range = namedtuple('Range', 'start stop size')
|
Range = namedtuple('Range', 'start stop size')
|
||||||
MULTIPART_SEPARATOR = uuid.uuid4().hex
|
MULTIPART_SEPARATOR = uuid.uuid4().hex
|
||||||
@ -37,6 +42,14 @@ import zlib
|
|||||||
from itertools import zip_longest
|
from itertools import zip_longest
|
||||||
|
|
||||||
|
|
||||||
|
def file_metadata(fileobj):
|
||||||
|
try:
|
||||||
|
fd = fileobj.fileno()
|
||||||
|
return os.fstat(fd)
|
||||||
|
except Exception:
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
def header_list_to_file(buf): # {{{
|
def header_list_to_file(buf): # {{{
|
||||||
buf.append('')
|
buf.append('')
|
||||||
return ReadOnlyFileBuffer(b''.join((x + '\r\n').encode('ascii') for x in buf))
|
return ReadOnlyFileBuffer(b''.join((x + '\r\n').encode('ascii') for x in buf))
|
||||||
@ -374,17 +387,15 @@ class HTTPConnection(HTTPRequest):
|
|||||||
if limit <= 0:
|
if limit <= 0:
|
||||||
return True
|
return True
|
||||||
if self.use_sendfile and not isinstance(buf, (BytesIO, ReadOnlyFileBuffer)):
|
if self.use_sendfile and not isinstance(buf, (BytesIO, ReadOnlyFileBuffer)):
|
||||||
|
limit = min(limit, 2 ** 30)
|
||||||
try:
|
try:
|
||||||
sent = sendfile_to_socket_async(buf, pos, limit, self.socket)
|
sent = os.sendfile(self.socket.fileno(), buf.fileno(), pos, limit)
|
||||||
except CannotSendfile:
|
except OSError as e:
|
||||||
self.use_sendfile = False
|
|
||||||
return False
|
|
||||||
except SendfileInterrupted:
|
|
||||||
return False
|
|
||||||
except IOError as e:
|
|
||||||
if e.errno in socket_errors_socket_closed:
|
if e.errno in socket_errors_socket_closed:
|
||||||
self.ready = self.use_sendfile = False
|
self.ready = self.use_sendfile = False
|
||||||
return False
|
return False
|
||||||
|
if e.errno in (errno.EAGAIN, errno.EINTR):
|
||||||
|
return False
|
||||||
raise
|
raise
|
||||||
finally:
|
finally:
|
||||||
self.last_activity = monotonic()
|
self.last_activity = monotonic()
|
||||||
@ -561,7 +572,7 @@ class HTTPConnection(HTTPRequest):
|
|||||||
self.reset_state()
|
self.reset_state()
|
||||||
return
|
return
|
||||||
if isinstance(output, ReadableOutput):
|
if isinstance(output, ReadableOutput):
|
||||||
self.use_sendfile = output.use_sendfile and self.opts.use_sendfile and sendfile_to_socket_async is not None and self.ssl_context is None
|
self.use_sendfile = output.use_sendfile and self.opts.use_sendfile and hasattr(os, 'sendfile') and self.ssl_context is None
|
||||||
# sendfile() does not work with SSL sockets since encryption has to
|
# sendfile() does not work with SSL sockets since encryption has to
|
||||||
# be done in userspace
|
# be done in userspace
|
||||||
if output.ranges is not None:
|
if output.ranges is not None:
|
||||||
|
@ -1,133 +0,0 @@
|
|||||||
#!/usr/bin/env python
|
|
||||||
# vim:fileencoding=utf-8
|
|
||||||
|
|
||||||
|
|
||||||
__license__ = 'GPL v3'
|
|
||||||
__copyright__ = '2015, Kovid Goyal <kovid at kovidgoyal.net>'
|
|
||||||
|
|
||||||
import os, ctypes, errno, socket
|
|
||||||
from io import DEFAULT_BUFFER_SIZE
|
|
||||||
from select import select
|
|
||||||
|
|
||||||
from calibre.constants import islinux, ismacos
|
|
||||||
from calibre.srv.utils import eintr_retry_call
|
|
||||||
|
|
||||||
|
|
||||||
def file_metadata(fileobj):
|
|
||||||
try:
|
|
||||||
fd = fileobj.fileno()
|
|
||||||
return os.fstat(fd)
|
|
||||||
except Exception:
|
|
||||||
pass
|
|
||||||
|
|
||||||
|
|
||||||
def copy_range(src_file, start, size, dest):
|
|
||||||
total_sent = 0
|
|
||||||
src_file.seek(start)
|
|
||||||
while size > 0:
|
|
||||||
data = eintr_retry_call(src_file.read, min(size, DEFAULT_BUFFER_SIZE))
|
|
||||||
if len(data) == 0:
|
|
||||||
break # EOF
|
|
||||||
dest.write(data)
|
|
||||||
size -= len(data)
|
|
||||||
total_sent += len(data)
|
|
||||||
del data
|
|
||||||
return total_sent
|
|
||||||
|
|
||||||
|
|
||||||
class CannotSendfile(Exception):
|
|
||||||
pass
|
|
||||||
|
|
||||||
|
|
||||||
class SendfileInterrupted(Exception):
|
|
||||||
pass
|
|
||||||
|
|
||||||
|
|
||||||
sendfile_to_socket = sendfile_to_socket_async = None
|
|
||||||
|
|
||||||
if ismacos:
|
|
||||||
libc = ctypes.CDLL(None, use_errno=True)
|
|
||||||
sendfile = ctypes.CFUNCTYPE(
|
|
||||||
ctypes.c_int, ctypes.c_int, ctypes.c_int, ctypes.c_int64, ctypes.POINTER(ctypes.c_int64), ctypes.c_void_p, ctypes.c_int, use_errno=True)(
|
|
||||||
('sendfile', libc))
|
|
||||||
del libc
|
|
||||||
|
|
||||||
def sendfile_to_socket(fileobj, offset, size, socket_file):
|
|
||||||
timeout = socket_file.gettimeout()
|
|
||||||
if timeout == 0:
|
|
||||||
return copy_range(fileobj, offset, size, socket_file)
|
|
||||||
num_bytes = ctypes.c_int64(size)
|
|
||||||
total_sent = 0
|
|
||||||
while size > 0:
|
|
||||||
num_bytes.value = size
|
|
||||||
r, w, x = select([], [socket_file], [], timeout)
|
|
||||||
if not w:
|
|
||||||
raise socket.timeout('timed out in sendfile() waiting for socket to become writeable')
|
|
||||||
ret = sendfile(fileobj.fileno(), socket_file.fileno(), offset, ctypes.byref(num_bytes), None, 0)
|
|
||||||
if ret != 0:
|
|
||||||
err = ctypes.get_errno()
|
|
||||||
if err in (errno.EBADF, errno.ENOTSUP, errno.ENOTSOCK, errno.EOPNOTSUPP):
|
|
||||||
return copy_range(fileobj, offset, size, socket_file)
|
|
||||||
if err not in (errno.EINTR, errno.EAGAIN):
|
|
||||||
raise IOError((err, os.strerror(err)))
|
|
||||||
if num_bytes.value == 0:
|
|
||||||
break # EOF
|
|
||||||
total_sent += num_bytes.value
|
|
||||||
size -= num_bytes.value
|
|
||||||
offset += num_bytes.value
|
|
||||||
return total_sent
|
|
||||||
|
|
||||||
def sendfile_to_socket_async(fileobj, offset, size, socket_file):
|
|
||||||
num_bytes = ctypes.c_int64(size)
|
|
||||||
ret = sendfile(fileobj.fileno(), socket_file.fileno(), offset, ctypes.byref(num_bytes), None, 0)
|
|
||||||
if ret != 0:
|
|
||||||
err = ctypes.get_errno()
|
|
||||||
if err in (errno.EBADF, errno.ENOTSUP, errno.ENOTSOCK, errno.EOPNOTSUPP):
|
|
||||||
raise CannotSendfile()
|
|
||||||
if err == errno.EINTR:
|
|
||||||
raise SendfileInterrupted()
|
|
||||||
if err != errno.EAGAIN:
|
|
||||||
raise IOError((err, os.strerror(err)))
|
|
||||||
return num_bytes.value
|
|
||||||
|
|
||||||
elif islinux:
|
|
||||||
libc = ctypes.CDLL(None, use_errno=True)
|
|
||||||
sendfile = ctypes.CFUNCTYPE(
|
|
||||||
ctypes.c_ssize_t, ctypes.c_int, ctypes.c_int, ctypes.POINTER(ctypes.c_int64), ctypes.c_size_t, use_errno=True)(('sendfile64', libc))
|
|
||||||
del libc
|
|
||||||
|
|
||||||
def sendfile_to_socket(fileobj, offset, size, socket_file):
|
|
||||||
off = ctypes.c_int64(offset)
|
|
||||||
timeout = socket_file.gettimeout()
|
|
||||||
if timeout == 0:
|
|
||||||
return copy_range(fileobj, off.value, size, socket_file)
|
|
||||||
total_sent = 0
|
|
||||||
while size > 0:
|
|
||||||
r, w, x = select([], [socket_file], [], timeout)
|
|
||||||
if not w:
|
|
||||||
raise socket.timeout('timed out in sendfile() waiting for socket to become writeable')
|
|
||||||
sent = sendfile(socket_file.fileno(), fileobj.fileno(), ctypes.byref(off), size)
|
|
||||||
if sent < 0:
|
|
||||||
err = ctypes.get_errno()
|
|
||||||
if err in (errno.ENOSYS, errno.EINVAL):
|
|
||||||
return copy_range(fileobj, off.value, size, socket_file)
|
|
||||||
if err not in (errno.EINTR, errno.EAGAIN):
|
|
||||||
raise IOError((err, os.strerror(err)))
|
|
||||||
elif sent == 0:
|
|
||||||
break # EOF
|
|
||||||
else:
|
|
||||||
size -= sent
|
|
||||||
total_sent += sent
|
|
||||||
return total_sent
|
|
||||||
|
|
||||||
def sendfile_to_socket_async(fileobj, offset, size, socket_file):
|
|
||||||
off = ctypes.c_int64(offset)
|
|
||||||
sent = sendfile(socket_file.fileno(), fileobj.fileno(), ctypes.byref(off), size)
|
|
||||||
if sent < 0:
|
|
||||||
err = ctypes.get_errno()
|
|
||||||
if err in (errno.ENOSYS, errno.EINVAL):
|
|
||||||
raise CannotSendfile()
|
|
||||||
if err in (errno.EINTR, errno.EAGAIN):
|
|
||||||
raise SendfileInterrupted()
|
|
||||||
raise IOError((err, os.strerror(err)))
|
|
||||||
return sent
|
|
Loading…
x
Reference in New Issue
Block a user