mirror of
https://github.com/kovidgoyal/calibre.git
synced 2025-07-09 03:04:10 -04:00
Improve performance of sending of large files when sendfile() is not available (such as on windows)
Use the kernel socket send buffer size as the blocksize for sends.
This commit is contained in:
parent
eb72da611d
commit
51b8281151
@ -154,7 +154,6 @@ class HTTPRequest(Connection):
|
|||||||
|
|
||||||
def __init__(self, *args, **kwargs):
|
def __init__(self, *args, **kwargs):
|
||||||
Connection.__init__(self, *args, **kwargs)
|
Connection.__init__(self, *args, **kwargs)
|
||||||
self.corked = False
|
|
||||||
self.max_header_line_size = int(1024 * self.opts.max_header_line_size)
|
self.max_header_line_size = int(1024 * self.opts.max_header_line_size)
|
||||||
self.max_request_body_size = int(1024 * 1024 * self.opts.max_request_body_size)
|
self.max_request_body_size = int(1024 * 1024 * self.opts.max_request_body_size)
|
||||||
|
|
||||||
|
@ -7,10 +7,6 @@ __license__ = 'GPL v3'
|
|||||||
__copyright__ = '2015, Kovid Goyal <kovid at kovidgoyal.net>'
|
__copyright__ = '2015, Kovid Goyal <kovid at kovidgoyal.net>'
|
||||||
|
|
||||||
import os, httplib, hashlib, uuid, zlib, time, struct, repr as reprlib
|
import os, httplib, hashlib, uuid, zlib, time, struct, repr as reprlib
|
||||||
try:
|
|
||||||
from select import PIPE_BUF
|
|
||||||
except ImportError:
|
|
||||||
PIPE_BUF = 512 # windows
|
|
||||||
from collections import namedtuple
|
from collections import namedtuple
|
||||||
from io import BytesIO, DEFAULT_BUFFER_SIZE
|
from io import BytesIO, DEFAULT_BUFFER_SIZE
|
||||||
from itertools import chain, repeat, izip_longest
|
from itertools import chain, repeat, izip_longest
|
||||||
@ -23,7 +19,7 @@ from calibre.srv.loop import WRITE
|
|||||||
from calibre.srv.errors import HTTP404
|
from calibre.srv.errors import HTTP404
|
||||||
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.sendfile import file_metadata, sendfile_to_socket_async, CannotSendfile, SendfileInterrupted
|
||||||
from calibre.srv.utils import MultiDict, start_cork, stop_cork, http_date, HTTP1, HTTP11, socket_errors_socket_closed
|
from calibre.srv.utils import MultiDict, http_date, HTTP1, HTTP11, socket_errors_socket_closed
|
||||||
from calibre.utils.monotonic import monotonic
|
from calibre.utils.monotonic import monotonic
|
||||||
|
|
||||||
Range = namedtuple('Range', 'start stop size')
|
Range = namedtuple('Range', 'start stop size')
|
||||||
@ -283,7 +279,7 @@ class HTTPConnection(HTTPRequest):
|
|||||||
self.use_sendfile = self.ready = False
|
self.use_sendfile = self.ready = False
|
||||||
raise IOError('sendfile() failed to write any bytes to the socket')
|
raise IOError('sendfile() failed to write any bytes to the socket')
|
||||||
else:
|
else:
|
||||||
sent = self.send(buf.read(min(limit, PIPE_BUF)))
|
sent = self.send(buf.read(min(limit, self.send_bufsize)))
|
||||||
buf.seek(pos + sent)
|
buf.seek(pos + sent)
|
||||||
return buf.tell() == end
|
return buf.tell() == end
|
||||||
|
|
||||||
@ -367,8 +363,7 @@ class HTTPConnection(HTTPRequest):
|
|||||||
|
|
||||||
def response_ready(self, header_file, output=None):
|
def response_ready(self, header_file, output=None):
|
||||||
self.response_started = True
|
self.response_started = True
|
||||||
start_cork(self.socket)
|
self.optimize_for_sending_packet()
|
||||||
self.corked = True
|
|
||||||
self.use_sendfile = False
|
self.use_sendfile = False
|
||||||
self.set_state(WRITE, self.write_response_headers, header_file, output)
|
self.set_state(WRITE, self.write_response_headers, header_file, output)
|
||||||
|
|
||||||
@ -437,8 +432,7 @@ class HTTPConnection(HTTPRequest):
|
|||||||
def reset_state(self):
|
def reset_state(self):
|
||||||
self.connection_ready()
|
self.connection_ready()
|
||||||
self.ready = not self.close_after_response
|
self.ready = not self.close_after_response
|
||||||
stop_cork(self.socket)
|
self.end_send_optimization()
|
||||||
self.corked = False
|
|
||||||
|
|
||||||
def report_unhandled_exception(self, e, formatted_traceback):
|
def report_unhandled_exception(self, e, formatted_traceback):
|
||||||
self.simple_response(httplib.INTERNAL_SERVER_ERROR)
|
self.simple_response(httplib.INTERNAL_SERVER_ERROR)
|
||||||
|
@ -14,17 +14,20 @@ from calibre import as_unicode
|
|||||||
from calibre.ptempfile import TemporaryDirectory
|
from calibre.ptempfile import TemporaryDirectory
|
||||||
from calibre.srv.opts import Options
|
from calibre.srv.opts import Options
|
||||||
from calibre.srv.utils import (
|
from calibre.srv.utils import (
|
||||||
socket_errors_socket_closed, socket_errors_nonblocking, HandleInterrupt, socket_errors_eintr)
|
socket_errors_socket_closed, socket_errors_nonblocking, HandleInterrupt,
|
||||||
|
socket_errors_eintr, start_cork, stop_cork)
|
||||||
from calibre.utils.socket_inheritance import set_socket_inherit
|
from calibre.utils.socket_inheritance import set_socket_inherit
|
||||||
from calibre.utils.logging import ThreadSafeLog
|
from calibre.utils.logging import ThreadSafeLog
|
||||||
from calibre.utils.monotonic import monotonic
|
from calibre.utils.monotonic import monotonic
|
||||||
|
|
||||||
READ, WRITE, RDWR = 'READ', 'WRITE', 'RDWR'
|
READ, WRITE, RDWR = 'READ', 'WRITE', 'RDWR'
|
||||||
|
DESIRED_SEND_BUFFER_SIZE = 16 * 1024
|
||||||
|
|
||||||
class Connection(object):
|
class Connection(object):
|
||||||
|
|
||||||
def __init__(self, socket, opts, ssl_context, tdir):
|
def __init__(self, socket, opts, ssl_context, tdir):
|
||||||
self.opts = opts
|
self.opts = opts
|
||||||
|
self.orig_send_bufsize = self.send_bufsize = 4096
|
||||||
self.tdir = tdir
|
self.tdir = tdir
|
||||||
self.ssl_context = ssl_context
|
self.ssl_context = ssl_context
|
||||||
self.wait_for = READ
|
self.wait_for = READ
|
||||||
@ -39,6 +42,22 @@ class Connection(object):
|
|||||||
self.connection_ready()
|
self.connection_ready()
|
||||||
self.last_activity = monotonic()
|
self.last_activity = monotonic()
|
||||||
|
|
||||||
|
def optimize_for_sending_packet(self):
|
||||||
|
start_cork(self.socket)
|
||||||
|
self.orig_send_bufsize = self.send_bufsize = self.socket.getsockopt(socket.SOL_SOCKET, socket.SO_SNDBUF)
|
||||||
|
if self.send_bufsize < DESIRED_SEND_BUFFER_SIZE:
|
||||||
|
try:
|
||||||
|
self.socket.setsockopt(socket.SOL_SOCKET, socket.SO_SNDBUF, DESIRED_SEND_BUFFER_SIZE)
|
||||||
|
except socket.error:
|
||||||
|
pass
|
||||||
|
else:
|
||||||
|
self.send_bufsize = self.socket.getsockopt(socket.SOL_SOCKET, socket.SO_SNDBUF)
|
||||||
|
|
||||||
|
def end_send_optimization(self):
|
||||||
|
stop_cork(self.socket)
|
||||||
|
if self.send_bufsize != self.orig_send_bufsize:
|
||||||
|
self.socket.setsockopt(socket.SOL_SOCKET, socket.SO_SNDBUF, self.orig_send_bufsize)
|
||||||
|
|
||||||
def set_state(self, wait_for, func, *args, **kwargs):
|
def set_state(self, wait_for, func, *args, **kwargs):
|
||||||
self.wait_for = wait_for
|
self.wait_for = wait_for
|
||||||
if args or kwargs:
|
if args or kwargs:
|
||||||
|
@ -329,6 +329,6 @@ class TestHTTP(BaseTest):
|
|||||||
self.ae(len(data), len(rdata))
|
self.ae(len(data), len(rdata))
|
||||||
self.ae(hashlib.sha1(data).hexdigest(), hashlib.sha1(rdata).hexdigest())
|
self.ae(hashlib.sha1(data).hexdigest(), hashlib.sha1(rdata).hexdigest())
|
||||||
self.ae(data, rdata)
|
self.ae(data, rdata)
|
||||||
self.assertLess(monotonic() - start_time, 5, 'Large file transfer took too long')
|
self.assertLess(monotonic() - start_time, 1, 'Large file transfer took too long')
|
||||||
|
|
||||||
# }}}
|
# }}}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user