diff --git a/src/poetry/inspection/lazy_wheel.py b/src/poetry/inspection/lazy_wheel.py index 32c0d9b2c05..2ec2e537d92 100644 --- a/src/poetry/inspection/lazy_wheel.py +++ b/src/poetry/inspection/lazy_wheel.py @@ -49,6 +49,11 @@ class HTTPRangeRequestUnsupported(LazyWheelUnsupportedError): """Raised when the remote server appears unable to support byte ranges.""" +class HTTPRangeRequestNotRespected(LazyWheelUnsupportedError): + """Raised when the remote server tells us that it supports byte ranges + but does not respect a respective request.""" + + class UnsupportedWheel(LazyWheelUnsupportedError): """Unsupported wheel.""" @@ -429,7 +434,12 @@ def _stream_response(self, start: int, end: int) -> Response: self._request_count += 1 response = self._session.get(self._url, headers=headers, stream=True) response.raise_for_status() - assert int(response.headers["Content-Length"]) == (end - start + 1) + if int(response.headers["Content-Length"]) != (end - start + 1): + raise HTTPRangeRequestNotRespected( + f"server did not respect byte range request: " + f"requested {end - start + 1} bytes, got " + f"{response.headers['Content-Length']} bytes" + ) return response def _fetch_content_range(self, start: int, end: int) -> Iterator[bytes]: diff --git a/tests/inspection/test_lazy_wheel.py b/tests/inspection/test_lazy_wheel.py index a0e8385833a..36279039cfa 100644 --- a/tests/inspection/test_lazy_wheel.py +++ b/tests/inspection/test_lazy_wheel.py @@ -14,6 +14,7 @@ from requests import codes +from poetry.inspection.lazy_wheel import HTTPRangeRequestNotRespected from poetry.inspection.lazy_wheel import HTTPRangeRequestUnsupported from poetry.inspection.lazy_wheel import InvalidWheel from poetry.inspection.lazy_wheel import LazyWheelUnsupportedError @@ -38,6 +39,7 @@ def __call__( *, accept_ranges: str | None = "bytes", negative_offset_error: tuple[int, bytes] | None = None, + ignore_accept_ranges: bool = False, ) -> HTTPrettyRequestCallback: ... class AssertMetadataFromWheelUrl(Protocol): @@ -113,6 +115,7 @@ def _factory( *, accept_ranges: str | None = "bytes", negative_offset_error: tuple[int, bytes] | None = None, + ignore_accept_ranges: bool = False, ) -> HTTPrettyRequestCallback: def handle_request( request: HTTPrettyRequest, uri: str, response_headers: dict[str, Any] @@ -156,7 +159,7 @@ def handle_request( response_headers, negative_offset_error[1], ) - if accept_ranges == "bytes" and rng: + if accept_ranges == "bytes" and rng and not ignore_accept_ranges: return build_partial_response( rng, wheel_bytes, @@ -404,6 +407,31 @@ def test_metadata_from_wheel_url_range_requests_not_supported_two_requests( assert latest_requests[1].method == "HEAD" +def test_metadata_from_wheel_url_range_requests_supported_but_not_respected( + http: type[httpretty.httpretty], + handle_request_factory: RequestCallbackFactory, +) -> None: + domain = "range-requests-not-respected.com" + uri_regex = re.compile(f"^https://{domain}/.*$") + request_callback = handle_request_factory( + negative_offset_error=(codes.method_not_allowed, b"Method not allowed"), + ignore_accept_ranges=True, + ) + http.register_uri(http.GET, uri_regex, body=request_callback) + http.register_uri(http.HEAD, uri_regex, body=request_callback) + + url = f"https://{domain}/poetry_core-1.5.0-py3-none-any.whl" + + with pytest.raises(HTTPRangeRequestNotRespected): + metadata_from_wheel_url("poetry-core", url, requests.Session()) + + latest_requests = http.latest_requests() + assert len(latest_requests) == 3 + assert latest_requests[0].method == "GET" + assert latest_requests[1].method == "HEAD" + assert latest_requests[2].method == "GET" + + def test_metadata_from_wheel_url_invalid_wheel( http: type[httpretty.httpretty], handle_request_factory: RequestCallbackFactory,