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