1# Copyright 2017 Google Inc. 2# 3# Licensed under the Apache License, Version 2.0 (the "License"); 4# you may not use this file except in compliance with the License. 5# You may obtain a copy of the License at 6# 7# http://www.apache.org/licenses/LICENSE-2.0 8# 9# Unless required by applicable law or agreed to in writing, software 10# distributed under the License is distributed on an "AS IS" BASIS, 11# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12# See the License for the specific language governing permissions and 13# limitations under the License. 14 15import http.client 16import io 17 18import mock 19import pytest 20 21from google.resumable_media import _download 22from google.resumable_media import common 23 24 25EXAMPLE_URL = ( 26 "https://www.googleapis.com/download/storage/v1/b/{BUCKET}/o/{OBJECT}?alt=media" 27) 28 29 30class TestDownloadBase(object): 31 def test_constructor_defaults(self): 32 download = _download.DownloadBase(EXAMPLE_URL) 33 assert download.media_url == EXAMPLE_URL 34 assert download._stream is None 35 assert download.start is None 36 assert download.end is None 37 assert download._headers == {} 38 assert not download._finished 39 _check_retry_strategy(download) 40 41 def test_constructor_explicit(self): 42 start = 11 43 end = 10001 44 headers = {"foof": "barf"} 45 download = _download.DownloadBase( 46 EXAMPLE_URL, 47 stream=mock.sentinel.stream, 48 start=start, 49 end=end, 50 headers=headers, 51 ) 52 assert download.media_url == EXAMPLE_URL 53 assert download._stream is mock.sentinel.stream 54 assert download.start == start 55 assert download.end == end 56 assert download._headers is headers 57 assert not download._finished 58 _check_retry_strategy(download) 59 60 def test_finished_property(self): 61 download = _download.DownloadBase(EXAMPLE_URL) 62 # Default value of @property. 63 assert not download.finished 64 65 # Make sure we cannot set it on public @property. 66 with pytest.raises(AttributeError): 67 download.finished = False 68 69 # Set it privately and then check the @property. 70 download._finished = True 71 assert download.finished 72 73 def test__get_status_code(self): 74 with pytest.raises(NotImplementedError) as exc_info: 75 _download.DownloadBase._get_status_code(None) 76 77 exc_info.match("virtual") 78 79 def test__get_headers(self): 80 with pytest.raises(NotImplementedError) as exc_info: 81 _download.DownloadBase._get_headers(None) 82 83 exc_info.match("virtual") 84 85 def test__get_body(self): 86 with pytest.raises(NotImplementedError) as exc_info: 87 _download.DownloadBase._get_body(None) 88 89 exc_info.match("virtual") 90 91 92class TestDownload(object): 93 def test__prepare_request_already_finished(self): 94 download = _download.Download(EXAMPLE_URL) 95 download._finished = True 96 with pytest.raises(ValueError): 97 download._prepare_request() 98 99 def test__prepare_request(self): 100 download1 = _download.Download(EXAMPLE_URL) 101 method1, url1, payload1, headers1 = download1._prepare_request() 102 assert method1 == "GET" 103 assert url1 == EXAMPLE_URL 104 assert payload1 is None 105 assert headers1 == {} 106 107 download2 = _download.Download(EXAMPLE_URL, start=53) 108 method2, url2, payload2, headers2 = download2._prepare_request() 109 assert method2 == "GET" 110 assert url2 == EXAMPLE_URL 111 assert payload2 is None 112 assert headers2 == {"range": "bytes=53-"} 113 114 def test__prepare_request_with_headers(self): 115 headers = {"spoonge": "borb"} 116 download = _download.Download(EXAMPLE_URL, start=11, end=111, headers=headers) 117 method, url, payload, new_headers = download._prepare_request() 118 assert method == "GET" 119 assert url == EXAMPLE_URL 120 assert payload is None 121 assert new_headers is headers 122 assert headers == {"range": "bytes=11-111", "spoonge": "borb"} 123 124 def test__process_response(self): 125 download = _download.Download(EXAMPLE_URL) 126 _fix_up_virtual(download) 127 128 # Make sure **not finished** before. 129 assert not download.finished 130 response = mock.Mock(status_code=int(http.client.OK), spec=["status_code"]) 131 ret_val = download._process_response(response) 132 assert ret_val is None 133 # Make sure **finished** after. 134 assert download.finished 135 136 def test__process_response_bad_status(self): 137 download = _download.Download(EXAMPLE_URL) 138 _fix_up_virtual(download) 139 140 # Make sure **not finished** before. 141 assert not download.finished 142 response = mock.Mock( 143 status_code=int(http.client.NOT_FOUND), spec=["status_code"] 144 ) 145 with pytest.raises(common.InvalidResponse) as exc_info: 146 download._process_response(response) 147 148 error = exc_info.value 149 assert error.response is response 150 assert len(error.args) == 5 151 assert error.args[1] == response.status_code 152 assert error.args[3] == http.client.OK 153 assert error.args[4] == http.client.PARTIAL_CONTENT 154 # Make sure **finished** even after a failure. 155 assert download.finished 156 157 def test_consume(self): 158 download = _download.Download(EXAMPLE_URL) 159 with pytest.raises(NotImplementedError) as exc_info: 160 download.consume(None) 161 162 exc_info.match("virtual") 163 164 165class TestChunkedDownload(object): 166 def test_constructor_defaults(self): 167 chunk_size = 256 168 stream = mock.sentinel.stream 169 download = _download.ChunkedDownload(EXAMPLE_URL, chunk_size, stream) 170 assert download.media_url == EXAMPLE_URL 171 assert download.chunk_size == chunk_size 172 assert download.start == 0 173 assert download.end is None 174 assert download._headers == {} 175 assert not download._finished 176 _check_retry_strategy(download) 177 assert download._stream is stream 178 assert download._bytes_downloaded == 0 179 assert download._total_bytes is None 180 assert not download._invalid 181 182 def test_constructor_bad_start(self): 183 with pytest.raises(ValueError): 184 _download.ChunkedDownload(EXAMPLE_URL, 256, None, start=-11) 185 186 def test_bytes_downloaded_property(self): 187 download = _download.ChunkedDownload(EXAMPLE_URL, 256, None) 188 # Default value of @property. 189 assert download.bytes_downloaded == 0 190 191 # Make sure we cannot set it on public @property. 192 with pytest.raises(AttributeError): 193 download.bytes_downloaded = 1024 194 195 # Set it privately and then check the @property. 196 download._bytes_downloaded = 128 197 assert download.bytes_downloaded == 128 198 199 def test_total_bytes_property(self): 200 download = _download.ChunkedDownload(EXAMPLE_URL, 256, None) 201 # Default value of @property. 202 assert download.total_bytes is None 203 204 # Make sure we cannot set it on public @property. 205 with pytest.raises(AttributeError): 206 download.total_bytes = 65536 207 208 # Set it privately and then check the @property. 209 download._total_bytes = 8192 210 assert download.total_bytes == 8192 211 212 def test__get_byte_range(self): 213 chunk_size = 512 214 download = _download.ChunkedDownload(EXAMPLE_URL, chunk_size, None) 215 curr_start, curr_end = download._get_byte_range() 216 assert curr_start == 0 217 assert curr_end == chunk_size - 1 218 219 def test__get_byte_range_with_end(self): 220 chunk_size = 512 221 start = 1024 222 end = 1151 223 download = _download.ChunkedDownload( 224 EXAMPLE_URL, chunk_size, None, start=start, end=end 225 ) 226 curr_start, curr_end = download._get_byte_range() 227 assert curr_start == start 228 assert curr_end == end 229 # Make sure this is less than the chunk size. 230 actual_size = curr_end - curr_start + 1 231 assert actual_size < chunk_size 232 233 def test__get_byte_range_with_total_bytes(self): 234 chunk_size = 512 235 download = _download.ChunkedDownload(EXAMPLE_URL, chunk_size, None) 236 total_bytes = 207 237 download._total_bytes = total_bytes 238 curr_start, curr_end = download._get_byte_range() 239 assert curr_start == 0 240 assert curr_end == total_bytes - 1 241 # Make sure this is less than the chunk size. 242 actual_size = curr_end - curr_start + 1 243 assert actual_size < chunk_size 244 245 @staticmethod 246 def _response_content_range(start_byte, end_byte, total_bytes): 247 return "bytes {:d}-{:d}/{:d}".format(start_byte, end_byte, total_bytes) 248 249 def _response_headers(self, start_byte, end_byte, total_bytes): 250 content_length = end_byte - start_byte + 1 251 resp_range = self._response_content_range(start_byte, end_byte, total_bytes) 252 return { 253 "content-length": "{:d}".format(content_length), 254 "content-range": resp_range, 255 } 256 257 def _mock_response( 258 self, start_byte, end_byte, total_bytes, content=None, status_code=None 259 ): 260 response_headers = self._response_headers(start_byte, end_byte, total_bytes) 261 return mock.Mock( 262 content=content, 263 headers=response_headers, 264 status_code=status_code, 265 spec=["content", "headers", "status_code"], 266 ) 267 268 def test__prepare_request_already_finished(self): 269 download = _download.ChunkedDownload(EXAMPLE_URL, 64, None) 270 download._finished = True 271 with pytest.raises(ValueError) as exc_info: 272 download._prepare_request() 273 274 assert exc_info.match("Download has finished.") 275 276 def test__prepare_request_invalid(self): 277 download = _download.ChunkedDownload(EXAMPLE_URL, 64, None) 278 download._invalid = True 279 with pytest.raises(ValueError) as exc_info: 280 download._prepare_request() 281 282 assert exc_info.match("Download is invalid and cannot be re-used.") 283 284 def test__prepare_request(self): 285 chunk_size = 2048 286 download1 = _download.ChunkedDownload(EXAMPLE_URL, chunk_size, None) 287 method1, url1, payload1, headers1 = download1._prepare_request() 288 assert method1 == "GET" 289 assert url1 == EXAMPLE_URL 290 assert payload1 is None 291 assert headers1 == {"range": "bytes=0-2047"} 292 293 download2 = _download.ChunkedDownload( 294 EXAMPLE_URL, chunk_size, None, start=19991 295 ) 296 download2._total_bytes = 20101 297 method2, url2, payload2, headers2 = download2._prepare_request() 298 assert method2 == "GET" 299 assert url2 == EXAMPLE_URL 300 assert payload2 is None 301 assert headers2 == {"range": "bytes=19991-20100"} 302 303 def test__prepare_request_with_headers(self): 304 chunk_size = 2048 305 headers = {"patrizio": "Starf-ish"} 306 download = _download.ChunkedDownload( 307 EXAMPLE_URL, chunk_size, None, headers=headers 308 ) 309 method, url, payload, new_headers = download._prepare_request() 310 assert method == "GET" 311 assert url == EXAMPLE_URL 312 assert payload is None 313 assert new_headers is headers 314 expected = {"patrizio": "Starf-ish", "range": "bytes=0-2047"} 315 assert headers == expected 316 317 def test__make_invalid(self): 318 download = _download.ChunkedDownload(EXAMPLE_URL, 512, None) 319 assert not download.invalid 320 download._make_invalid() 321 assert download.invalid 322 323 def test__process_response(self): 324 data = b"1234xyztL" * 37 # 9 * 37 == 33 325 chunk_size = len(data) 326 stream = io.BytesIO() 327 download = _download.ChunkedDownload(EXAMPLE_URL, chunk_size, stream) 328 _fix_up_virtual(download) 329 330 already = 22 331 download._bytes_downloaded = already 332 total_bytes = 4444 333 334 # Check internal state before. 335 assert not download.finished 336 assert download.bytes_downloaded == already 337 assert download.total_bytes is None 338 # Actually call the method to update. 339 response = self._mock_response( 340 already, 341 already + chunk_size - 1, 342 total_bytes, 343 content=data, 344 status_code=int(http.client.PARTIAL_CONTENT), 345 ) 346 download._process_response(response) 347 # Check internal state after. 348 assert not download.finished 349 assert download.bytes_downloaded == already + chunk_size 350 assert download.total_bytes == total_bytes 351 assert stream.getvalue() == data 352 353 def test__process_response_transfer_encoding(self): 354 data = b"1234xyztL" * 37 355 chunk_size = len(data) 356 stream = io.BytesIO() 357 download = _download.ChunkedDownload(EXAMPLE_URL, chunk_size, stream) 358 _fix_up_virtual(download) 359 360 already = 22 361 download._bytes_downloaded = already 362 total_bytes = 4444 363 364 # Check internal state before. 365 assert not download.finished 366 assert download.bytes_downloaded == already 367 assert download.total_bytes is None 368 assert not download.invalid 369 # Actually call the method to update. 370 response = self._mock_response( 371 already, 372 already + chunk_size - 1, 373 total_bytes, 374 content=data, 375 status_code=int(http.client.PARTIAL_CONTENT), 376 ) 377 response.headers["transfer-encoding"] = "chunked" 378 del response.headers["content-length"] 379 download._process_response(response) 380 # Check internal state after. 381 assert not download.finished 382 assert download.bytes_downloaded == already + chunk_size 383 assert download.total_bytes == total_bytes 384 assert stream.getvalue() == data 385 386 def test__process_response_bad_status(self): 387 chunk_size = 384 388 stream = mock.Mock(spec=["write"]) 389 download = _download.ChunkedDownload(EXAMPLE_URL, chunk_size, stream) 390 _fix_up_virtual(download) 391 392 total_bytes = 300 393 394 # Check internal state before. 395 assert not download.finished 396 assert download.bytes_downloaded == 0 397 assert download.total_bytes is None 398 # Actually call the method to update. 399 response = self._mock_response( 400 0, total_bytes - 1, total_bytes, status_code=int(http.client.NOT_FOUND) 401 ) 402 with pytest.raises(common.InvalidResponse) as exc_info: 403 download._process_response(response) 404 405 error = exc_info.value 406 assert error.response is response 407 assert len(error.args) == 5 408 assert error.args[1] == response.status_code 409 assert error.args[3] == http.client.OK 410 assert error.args[4] == http.client.PARTIAL_CONTENT 411 # Check internal state after. 412 assert not download.finished 413 assert download.bytes_downloaded == 0 414 assert download.total_bytes is None 415 assert download.invalid 416 stream.write.assert_not_called() 417 418 def test__process_response_missing_content_length(self): 419 download = _download.ChunkedDownload(EXAMPLE_URL, 256, None) 420 _fix_up_virtual(download) 421 422 # Check internal state before. 423 assert not download.finished 424 assert download.bytes_downloaded == 0 425 assert download.total_bytes is None 426 assert not download.invalid 427 # Actually call the method to update. 428 response = mock.Mock( 429 headers={"content-range": "bytes 0-99/99"}, 430 status_code=int(http.client.PARTIAL_CONTENT), 431 content=b"DEADBEEF", 432 spec=["headers", "status_code", "content"], 433 ) 434 with pytest.raises(common.InvalidResponse) as exc_info: 435 download._process_response(response) 436 437 error = exc_info.value 438 assert error.response is response 439 assert len(error.args) == 2 440 assert error.args[1] == "content-length" 441 # Check internal state after. 442 assert not download.finished 443 assert download.bytes_downloaded == 0 444 assert download.total_bytes is None 445 assert download.invalid 446 447 def test__process_response_bad_content_range(self): 448 download = _download.ChunkedDownload(EXAMPLE_URL, 256, None) 449 _fix_up_virtual(download) 450 451 # Check internal state before. 452 assert not download.finished 453 assert download.bytes_downloaded == 0 454 assert download.total_bytes is None 455 assert not download.invalid 456 # Actually call the method to update. 457 data = b"stuff" 458 headers = { 459 "content-length": "{:d}".format(len(data)), 460 "content-range": "kites x-y/58", 461 } 462 response = mock.Mock( 463 content=data, 464 headers=headers, 465 status_code=int(http.client.PARTIAL_CONTENT), 466 spec=["content", "headers", "status_code"], 467 ) 468 with pytest.raises(common.InvalidResponse) as exc_info: 469 download._process_response(response) 470 471 error = exc_info.value 472 assert error.response is response 473 assert len(error.args) == 3 474 assert error.args[1] == headers["content-range"] 475 # Check internal state after. 476 assert not download.finished 477 assert download.bytes_downloaded == 0 478 assert download.total_bytes is None 479 assert download.invalid 480 481 def test__process_response_body_wrong_length(self): 482 chunk_size = 10 483 stream = mock.Mock(spec=["write"]) 484 download = _download.ChunkedDownload(EXAMPLE_URL, chunk_size, stream) 485 _fix_up_virtual(download) 486 487 total_bytes = 100 488 489 # Check internal state before. 490 assert not download.finished 491 assert download.bytes_downloaded == 0 492 assert download.total_bytes is None 493 # Actually call the method to update. 494 data = b"not 10" 495 response = self._mock_response( 496 0, 497 chunk_size - 1, 498 total_bytes, 499 content=data, 500 status_code=int(http.client.PARTIAL_CONTENT), 501 ) 502 with pytest.raises(common.InvalidResponse) as exc_info: 503 download._process_response(response) 504 505 error = exc_info.value 506 assert error.response is response 507 assert len(error.args) == 5 508 assert error.args[2] == chunk_size 509 assert error.args[4] == len(data) 510 # Check internal state after. 511 assert not download.finished 512 assert download.bytes_downloaded == 0 513 assert download.total_bytes is None 514 assert download.invalid 515 stream.write.assert_not_called() 516 517 def test__process_response_when_finished(self): 518 chunk_size = 256 519 stream = io.BytesIO() 520 download = _download.ChunkedDownload(EXAMPLE_URL, chunk_size, stream) 521 _fix_up_virtual(download) 522 523 total_bytes = 200 524 525 # Check internal state before. 526 assert not download.finished 527 assert download.bytes_downloaded == 0 528 assert download.total_bytes is None 529 # Actually call the method to update. 530 data = b"abcd" * 50 # 4 * 50 == 200 531 response = self._mock_response( 532 0, 533 total_bytes - 1, 534 total_bytes, 535 content=data, 536 status_code=int(http.client.OK), 537 ) 538 download._process_response(response) 539 # Check internal state after. 540 assert download.finished 541 assert download.bytes_downloaded == total_bytes 542 assert total_bytes < chunk_size 543 assert download.total_bytes == total_bytes 544 assert stream.getvalue() == data 545 546 def test__process_response_when_reaching_end(self): 547 chunk_size = 8192 548 end = 65000 549 stream = io.BytesIO() 550 download = _download.ChunkedDownload(EXAMPLE_URL, chunk_size, stream, end=end) 551 _fix_up_virtual(download) 552 553 download._bytes_downloaded = 7 * chunk_size 554 download._total_bytes = 8 * chunk_size 555 556 # Check internal state before. 557 assert not download.finished 558 assert download.bytes_downloaded == 7 * chunk_size 559 assert download.total_bytes == 8 * chunk_size 560 # Actually call the method to update. 561 expected_size = end - 7 * chunk_size + 1 562 data = b"B" * expected_size 563 response = self._mock_response( 564 7 * chunk_size, 565 end, 566 8 * chunk_size, 567 content=data, 568 status_code=int(http.client.PARTIAL_CONTENT), 569 ) 570 download._process_response(response) 571 # Check internal state after. 572 assert download.finished 573 assert download.bytes_downloaded == end + 1 574 assert download.bytes_downloaded < download.total_bytes 575 assert download.total_bytes == 8 * chunk_size 576 assert stream.getvalue() == data 577 578 def test__process_response_when_content_range_is_zero(self): 579 chunk_size = 10 580 stream = mock.Mock(spec=["write"]) 581 download = _download.ChunkedDownload(EXAMPLE_URL, chunk_size, stream) 582 _fix_up_virtual(download) 583 584 content_range = _download._ZERO_CONTENT_RANGE_HEADER 585 headers = {"content-range": content_range} 586 status_code = http.client.REQUESTED_RANGE_NOT_SATISFIABLE 587 response = mock.Mock( 588 headers=headers, status_code=status_code, spec=["headers", "status_code"] 589 ) 590 download._process_response(response) 591 stream.write.assert_not_called() 592 assert download.finished 593 assert download.bytes_downloaded == 0 594 assert download.total_bytes is None 595 596 def test_consume_next_chunk(self): 597 download = _download.ChunkedDownload(EXAMPLE_URL, 256, None) 598 with pytest.raises(NotImplementedError) as exc_info: 599 download.consume_next_chunk(None) 600 601 exc_info.match("virtual") 602 603 604class Test__add_bytes_range(object): 605 def test_do_nothing(self): 606 headers = {} 607 ret_val = _download.add_bytes_range(None, None, headers) 608 assert ret_val is None 609 assert headers == {} 610 611 def test_both_vals(self): 612 headers = {} 613 ret_val = _download.add_bytes_range(17, 1997, headers) 614 assert ret_val is None 615 assert headers == {"range": "bytes=17-1997"} 616 617 def test_end_only(self): 618 headers = {} 619 ret_val = _download.add_bytes_range(None, 909, headers) 620 assert ret_val is None 621 assert headers == {"range": "bytes=0-909"} 622 623 def test_start_only(self): 624 headers = {} 625 ret_val = _download.add_bytes_range(3735928559, None, headers) 626 assert ret_val is None 627 assert headers == {"range": "bytes=3735928559-"} 628 629 def test_start_as_offset(self): 630 headers = {} 631 ret_val = _download.add_bytes_range(-123454321, None, headers) 632 assert ret_val is None 633 assert headers == {"range": "bytes=-123454321"} 634 635 636class Test_get_range_info(object): 637 @staticmethod 638 def _make_response(content_range): 639 headers = {"content-range": content_range} 640 return mock.Mock(headers=headers, spec=["headers"]) 641 642 def _success_helper(self, **kwargs): 643 content_range = "Bytes 7-11/42" 644 response = self._make_response(content_range) 645 start_byte, end_byte, total_bytes = _download.get_range_info( 646 response, _get_headers, **kwargs 647 ) 648 assert start_byte == 7 649 assert end_byte == 11 650 assert total_bytes == 42 651 652 def test_success(self): 653 self._success_helper() 654 655 def test_success_with_callback(self): 656 callback = mock.Mock(spec=[]) 657 self._success_helper(callback=callback) 658 callback.assert_not_called() 659 660 def _failure_helper(self, **kwargs): 661 content_range = "nope x-6/y" 662 response = self._make_response(content_range) 663 with pytest.raises(common.InvalidResponse) as exc_info: 664 _download.get_range_info(response, _get_headers, **kwargs) 665 666 error = exc_info.value 667 assert error.response is response 668 assert len(error.args) == 3 669 assert error.args[1] == content_range 670 671 def test_failure(self): 672 self._failure_helper() 673 674 def test_failure_with_callback(self): 675 callback = mock.Mock(spec=[]) 676 self._failure_helper(callback=callback) 677 callback.assert_called_once_with() 678 679 def _missing_header_helper(self, **kwargs): 680 response = mock.Mock(headers={}, spec=["headers"]) 681 with pytest.raises(common.InvalidResponse) as exc_info: 682 _download.get_range_info(response, _get_headers, **kwargs) 683 684 error = exc_info.value 685 assert error.response is response 686 assert len(error.args) == 2 687 assert error.args[1] == "content-range" 688 689 def test_missing_header(self): 690 self._missing_header_helper() 691 692 def test_missing_header_with_callback(self): 693 callback = mock.Mock(spec=[]) 694 self._missing_header_helper(callback=callback) 695 callback.assert_called_once_with() 696 697 698class Test__check_for_zero_content_range(object): 699 @staticmethod 700 def _make_response(content_range, status_code): 701 headers = {"content-range": content_range} 702 return mock.Mock( 703 headers=headers, status_code=status_code, spec=["headers", "status_code"] 704 ) 705 706 def test_status_code_416_and_test_content_range_zero_both(self): 707 content_range = _download._ZERO_CONTENT_RANGE_HEADER 708 status_code = http.client.REQUESTED_RANGE_NOT_SATISFIABLE 709 response = self._make_response(content_range, status_code) 710 assert _download._check_for_zero_content_range( 711 response, _get_status_code, _get_headers 712 ) 713 714 def test_status_code_416_only(self): 715 content_range = "bytes 2-5/3" 716 status_code = http.client.REQUESTED_RANGE_NOT_SATISFIABLE 717 response = self._make_response(content_range, status_code) 718 assert not _download._check_for_zero_content_range( 719 response, _get_status_code, _get_headers 720 ) 721 722 def test_content_range_zero_only(self): 723 content_range = _download._ZERO_CONTENT_RANGE_HEADER 724 status_code = http.client.OK 725 response = self._make_response(content_range, status_code) 726 assert not _download._check_for_zero_content_range( 727 response, _get_status_code, _get_headers 728 ) 729 730 731def _get_status_code(response): 732 return response.status_code 733 734 735def _get_headers(response): 736 return response.headers 737 738 739def _get_body(response): 740 return response.content 741 742 743def _fix_up_virtual(download): 744 download._get_status_code = _get_status_code 745 download._get_headers = _get_headers 746 download._get_body = _get_body 747 748 749def _check_retry_strategy(download): 750 retry_strategy = download._retry_strategy 751 assert isinstance(retry_strategy, common.RetryStrategy) 752 assert retry_strategy.max_sleep == common.MAX_SLEEP 753 assert retry_strategy.max_cumulative_retry == common.MAX_CUMULATIVE_RETRY 754 assert retry_strategy.max_retries is None 755