906
906
_req_handler_class = MultipleRangeWithoutContentLengthRequestHandler
909
class TruncatedMultipleRangeRequestHandler(
910
http_server.TestingHTTPRequestHandler):
911
"""Reply to multiple range requests truncating the last ones.
913
This server generates responses whose Content-Length describes all the
914
ranges, but fail to include the last ones leading to client short reads.
915
This has been observed randomly with lighttpd (bug #179368).
918
_truncated_ranges = 2
920
def get_multiple_ranges(self, file, file_size, ranges):
921
self.send_response(206)
922
self.send_header('Accept-Ranges', 'bytes')
924
self.send_header('Content-Type',
925
'multipart/byteranges; boundary=%s' % boundary)
926
boundary_line = '--%s\r\n' % boundary
927
# Calculate the Content-Length
929
for (start, end) in ranges:
930
content_length += len(boundary_line)
931
content_length += self._header_line_length(
932
'Content-type', 'application/octet-stream')
933
content_length += self._header_line_length(
934
'Content-Range', 'bytes %d-%d/%d' % (start, end, file_size))
935
content_length += len('\r\n') # end headers
936
content_length += end - start # + 1
937
content_length += len(boundary_line)
938
self.send_header('Content-length', content_length)
941
# Send the multipart body
943
for (start, end) in ranges:
944
self.wfile.write(boundary_line)
945
self.send_header('Content-type', 'application/octet-stream')
946
self.send_header('Content-Range', 'bytes %d-%d/%d'
947
% (start, end, file_size))
949
if cur + self._truncated_ranges >= len(ranges):
950
# Abruptly ends the response and close the connection
951
self.close_connection = 1
953
self.send_range_content(file, start, end - start + 1)
956
self.wfile.write(boundary_line)
959
class TestTruncatedMultipleRangeServer(TestSpecificRequestHandler):
961
_req_handler_class = TruncatedMultipleRangeRequestHandler
964
super(TestTruncatedMultipleRangeServer, self).setUp()
965
self.build_tree_contents([('a', '0123456789')],)
967
def test_readv_with_short_reads(self):
968
server = self.get_readonly_server()
969
t = self._transport(server.get_url())
970
# Force separate ranges for each offset
971
t._bytes_to_read_before_seek = 0
972
ireadv = iter(t.readv('a', ((0, 1), (2, 1), (4, 2), (9, 1))))
973
self.assertEqual((0, '0'), ireadv.next())
974
self.assertEqual((2, '2'), ireadv.next())
975
if not self._testing_pycurl():
976
# Only one request have been issued so far (except for pycurl that
977
# try to read the whole response at once)
978
self.assertEqual(1, server.GET_request_nb)
979
self.assertEqual((4, '45'), ireadv.next())
980
self.assertEqual((9, '9'), ireadv.next())
981
# Both implementations issue 3 requests but:
982
# - urllib does two multiple (4 ranges, then 2 ranges) then a single
984
# - pycurl does two multiple (4 ranges, 4 ranges) then a single range
985
self.assertEqual(3, server.GET_request_nb)
986
# Finally the client have tried a single range request and stays in
988
self.assertEqual('single', t._range_hint)
908
990
class LimitedRangeRequestHandler(http_server.TestingHTTPRequestHandler):
909
991
"""Errors out when range specifiers exceed the limit"""