This commit is contained in:
Kovid Goyal 2015-05-25 21:01:06 +05:30
parent aa4c31ef75
commit eb72da611d
3 changed files with 18 additions and 9 deletions

View File

@ -24,6 +24,7 @@ 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, start_cork, stop_cork, http_date, HTTP1, HTTP11, socket_errors_socket_closed
from calibre.utils.monotonic import monotonic
Range = namedtuple('Range', 'start stop size') Range = namedtuple('Range', 'start stop size')
MULTIPART_SEPARATOR = uuid.uuid4().hex.decode('ascii') MULTIPART_SEPARATOR = uuid.uuid4().hex.decode('ascii')
@ -274,6 +275,8 @@ class HTTPConnection(HTTPRequest):
self.ready = self.use_sendfile = False self.ready = self.use_sendfile = False
return False return False
raise raise
finally:
self.last_activity = monotonic()
if sent == 0: if sent == 0:
# Something bad happened, was the file modified on disk by # Something bad happened, was the file modified on disk by
# another process? # another process?

View File

@ -80,9 +80,10 @@ if isosx:
err = ctypes.get_errno() err = ctypes.get_errno()
if err in (errno.EBADF, errno.ENOTSUP, errno.ENOTSOCK, errno.EOPNOTSUPP): if err in (errno.EBADF, errno.ENOTSUP, errno.ENOTSOCK, errno.EOPNOTSUPP):
raise CannotSendfile() raise CannotSendfile()
if err in (errno.EINTR, errno.EAGAIN): if err == errno.EINTR:
raise SendfileInterrupted() raise SendfileInterrupted()
raise IOError((err, os.strerror(err))) if err != errno.EAGAIN:
raise IOError((err, os.strerror(err)))
return num_bytes.value return num_bytes.value
elif islinux: elif islinux:

View File

@ -12,6 +12,7 @@ from tempfile import NamedTemporaryFile
from calibre import guess_type from calibre import guess_type
from calibre.srv.tests.base import BaseTest, TestServer from calibre.srv.tests.base import BaseTest, TestServer
from calibre.utils.monotonic import monotonic
class TestHTTP(BaseTest): class TestHTTP(BaseTest):
@ -242,8 +243,8 @@ class TestHTTP(BaseTest):
from calibre.srv.http_response import parse_multipart_byterange from calibre.srv.http_response import parse_multipart_byterange
def handler(conn): def handler(conn):
return conn.generate_static_output('test', lambda : ''.join(conn.path)) return conn.generate_static_output('test', lambda : ''.join(conn.path))
with TestServer(handler, timeout=0.1, compress_min_size=0) as server, \ with NamedTemporaryFile(suffix='test.epub') as f, open(P('localization/locales.zip'), 'rb') as lf, \
NamedTemporaryFile(suffix='test.epub') as f, open(P('localization/locales.zip'), 'rb') as lf: TestServer(handler, timeout=0.2, compress_min_size=0) as server:
fdata = string.ascii_letters * 100 fdata = string.ascii_letters * 100
f.write(fdata), f.seek(0) f.write(fdata), f.seek(0)
@ -280,20 +281,21 @@ class TestHTTP(BaseTest):
conn.request('GET', '/test', headers={'Range':'bytes=2-25'}) conn.request('GET', '/test', headers={'Range':'bytes=2-25'})
r = conn.getresponse() r = conn.getresponse()
self.ae(r.status, httplib.PARTIAL_CONTENT)
self.ae(type('')(r.getheader('Accept-Ranges')), 'bytes') self.ae(type('')(r.getheader('Accept-Ranges')), 'bytes')
self.ae(type('')(r.getheader('Content-Range')), 'bytes 2-25/%d' % len(fdata)) self.ae(type('')(r.getheader('Content-Range')), 'bytes 2-25/%d' % len(fdata))
self.ae(int(r.getheader('Content-Length')), 24) self.ae(int(r.getheader('Content-Length')), 24)
self.ae(r.status, httplib.PARTIAL_CONTENT), self.ae(r.read(), fdata[2:26]) self.ae(r.read(), fdata[2:26])
conn.request('GET', '/test', headers={'Range':'bytes=100000-'}) conn.request('GET', '/test', headers={'Range':'bytes=100000-'})
r = conn.getresponse() r = conn.getresponse()
self.ae(type('')(r.getheader('Content-Range')), 'bytes */%d' % len(fdata))
self.ae(r.status, httplib.REQUESTED_RANGE_NOT_SATISFIABLE) self.ae(r.status, httplib.REQUESTED_RANGE_NOT_SATISFIABLE)
self.ae(type('')(r.getheader('Content-Range')), 'bytes */%d' % len(fdata))
conn.request('GET', '/test', headers={'Range':'bytes=25-50', 'If-Range':etag}) conn.request('GET', '/test', headers={'Range':'bytes=25-50', 'If-Range':etag})
r = conn.getresponse() r = conn.getresponse()
self.ae(int(r.getheader('Content-Length')), 26)
self.ae(r.status, httplib.PARTIAL_CONTENT), self.ae(r.read(), fdata[25:51]) self.ae(r.status, httplib.PARTIAL_CONTENT), self.ae(r.read(), fdata[25:51])
self.ae(int(r.getheader('Content-Length')), 26)
conn.request('GET', '/test', headers={'Range':'bytes=0-1000000'}) conn.request('GET', '/test', headers={'Range':'bytes=0-1000000'})
r = conn.getresponse() r = conn.getresponse()
@ -301,12 +303,13 @@ class TestHTTP(BaseTest):
conn.request('GET', '/test', headers={'Range':'bytes=25-50', 'If-Range':'"nomatch"'}) conn.request('GET', '/test', headers={'Range':'bytes=25-50', 'If-Range':'"nomatch"'})
r = conn.getresponse() r = conn.getresponse()
self.ae(r.status, httplib.OK), self.ae(r.read(), fdata)
self.assertFalse(r.getheader('Content-Range')) self.assertFalse(r.getheader('Content-Range'))
self.ae(int(r.getheader('Content-Length')), len(fdata)) self.ae(int(r.getheader('Content-Length')), len(fdata))
self.ae(r.status, httplib.OK), self.ae(r.read(), fdata)
conn.request('GET', '/test', headers={'Range':'bytes=0-25,26-50'}) conn.request('GET', '/test', headers={'Range':'bytes=0-25,26-50'})
r = conn.getresponse() r = conn.getresponse()
self.ae(r.status, httplib.PARTIAL_CONTENT)
clen = int(r.getheader('Content-Length')) clen = int(r.getheader('Content-Length'))
data = r.read() data = r.read()
self.ae(clen, len(data)) self.ae(clen, len(data))
@ -314,16 +317,18 @@ class TestHTTP(BaseTest):
self.ae(parse_multipart_byterange(buf, r.getheader('Content-Type')), [(0, fdata[:26]), (26, fdata[26:51])]) self.ae(parse_multipart_byterange(buf, r.getheader('Content-Type')), [(0, fdata[:26]), (26, fdata[26:51])])
# Test sending of larger file # Test sending of larger file
start_time = monotonic()
lf.seek(0) lf.seek(0)
data = lf.read() data = lf.read()
server.change_handler(lambda conn: lf) server.change_handler(lambda conn: lf)
conn = server.connect() conn = server.connect()
conn.request('GET', '/test') conn.request('GET', '/test')
r = conn.getresponse() r = conn.getresponse()
self.ae(r.status, httplib.OK)
rdata = r.read() rdata = r.read()
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')
# Now try it without sendfile
# }}} # }}}