From db5efc27c51ee2422bdb2ee794897531bdef3ac5 Mon Sep 17 00:00:00 2001 From: Brandon Salmon Date: Wed, 24 Jun 2015 12:06:59 -0700 Subject: [PATCH] Fixed off by one error in GetRange. --- apitools/base/py/transfer.py | 20 ++++++++++++++------ apitools/base/py/transfer_test.py | 30 ++++++++++++++++++++++++++++++ 2 files changed, 44 insertions(+), 6 deletions(-) diff --git a/apitools/base/py/transfer.py b/apitools/base/py/transfer.py index 35a4774a..f1802f04 100644 --- a/apitools/base/py/transfer.py +++ b/apitools/base/py/transfer.py @@ -366,10 +366,18 @@ def __ComputeEndByte(self, start, end=None, use_chunks=True): end_byte = end if use_chunks: alternate = start + self.chunksize - 1 - end_byte = min(end_byte, alternate) if end_byte else alternate + if end_byte is not None: + end_byte = min(end_byte, alternate) + else: + end_byte = alternate + if self.total_size: alternate = self.total_size - 1 - end_byte = min(end_byte, alternate) if end_byte else alternate + if end_byte is not None: + end_byte = min(end_byte, alternate) + else: + end_byte = alternate + return end_byte def __GetChunk(self, start, end, additional_headers=None): @@ -434,18 +442,18 @@ def GetRange(self, start, end=None, additional_headers=None, self.EnsureInitialized() progress_end_normalized = False if self.total_size is not None: - progress, end = self.__NormalizeStartEnd(start, end) + progress, end_byte = self.__NormalizeStartEnd(start, end) progress_end_normalized = True else: progress = start - while not progress_end_normalized or progress < end: - end_byte = self.__ComputeEndByte(progress, end=end, + while not progress_end_normalized or progress <= end_byte: + end_byte = self.__ComputeEndByte(progress, end=end_byte, use_chunks=use_chunks) response = self.__GetChunk(progress, end_byte, additional_headers=additional_headers) if not progress_end_normalized: self.__SetTotal(response.info) - progress, end = self.__NormalizeStartEnd(start, end) + progress, end_byte = self.__NormalizeStartEnd(start, end) progress_end_normalized = True response = self.__ProcessResponse(response) progress += response.length diff --git a/apitools/base/py/transfer_test.py b/apitools/base/py/transfer_test.py index 9d58b1ea..8e29f128 100644 --- a/apitools/base/py/transfer_test.py +++ b/apitools/base/py/transfer_test.py @@ -76,6 +76,36 @@ def testComputeEndByteSmallTotal(self): download._Download__ComputeEndByte(start), msg='Failed on start={0}'.format(start)) + def testGetRange(self): + for (start_byte, end_byte) in [(0, 25), (5, 15), (0, 0), (25, 25)]: + bytes_http = object() + http = object() + download_stream = six.StringIO() + download = transfer.Download.FromStream(download_stream, + total_size=26, + auto_transfer=False) + download.bytes_http = bytes_http + base_url = 'https://part.one/' + with mock.patch.object(http_wrapper, 'MakeRequest', + autospec=True) as make_request: + make_request.return_value = http_wrapper.Response( + info={ + 'content-range': 'bytes %d-%d/26' % + (start_byte, end_byte), + 'status': http_client.OK, + }, + content=string.ascii_lowercase[start_byte:end_byte+1], + request_url=base_url, + ) + request = http_wrapper.Request(url='https://part.one/') + download.InitializeDownload(request, http=http) + download.GetRange(start_byte, end_byte) + self.assertEqual(1, make_request.call_count) + received_request = make_request.call_args[0][1] + self.assertEqual(base_url, received_request.url) + self.assertRangeAndContentRangeCompatible( + received_request, make_request.return_value) + def testNonChunkedDownload(self): bytes_http = object() http = object()