Skip to content

Commit 625f42e

Browse files
authored
fix: properly close streaming requests when not completely consumed (#9899)
1 parent 21e30b1 commit 625f42e

File tree

2 files changed

+62
-42
lines changed

2 files changed

+62
-42
lines changed

src/poetry/inspection/lazy_wheel.py

Lines changed: 51 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -428,15 +428,20 @@ def _stream_response(self, start: int, end: int) -> Response:
428428
headers["Range"] = f"bytes={start}-{end}"
429429
logger.debug("streamed bytes request: %s", headers["Range"])
430430
self._request_count += 1
431+
431432
response = self._session.get(self._url, headers=headers, stream=True)
432-
response.raise_for_status()
433-
if int(response.headers["Content-Length"]) != (end - start + 1):
434-
raise HTTPRangeRequestNotRespectedError(
435-
f"server did not respect byte range request: "
436-
f"requested {end - start + 1} bytes, got "
437-
f"{response.headers['Content-Length']} bytes"
438-
)
439-
return response
433+
try:
434+
response.raise_for_status()
435+
if int(response.headers["Content-Length"]) != (end - start + 1):
436+
raise HTTPRangeRequestNotRespectedError(
437+
f"server did not respect byte range request: "
438+
f"requested {end - start + 1} bytes, got "
439+
f"{response.headers['Content-Length']} bytes"
440+
)
441+
return response
442+
except BaseException:
443+
response.close()
444+
raise
440445

441446
def _fetch_content_range(self, start: int, end: int) -> Iterator[bytes]:
442447
"""Perform a series of HTTP range requests to cover the specified byte range.
@@ -445,7 +450,8 @@ def _fetch_content_range(self, start: int, end: int) -> Iterator[bytes]:
445450
method must *include* the byte indexed at argument ``end`` (so e.g. ``0-1`` is 2
446451
bytes long, and the range can never be empty).
447452
"""
448-
yield from self._stream_response(start, end).iter_content(CONTENT_CHUNK_SIZE)
453+
with self._stream_response(start, end) as response:
454+
yield from response.iter_content(CONTENT_CHUNK_SIZE)
449455

450456
@contextmanager
451457
def _stay(self) -> Iterator[None]:
@@ -549,7 +555,7 @@ def _fetch_content_length(self) -> int:
549555
else:
550556
# If we *could* download some file contents, then write them to the end of
551557
# the file and set up our bisect boundaries by hand.
552-
with self._stay():
558+
with self._stay(), tail:
553559
response_length = int(tail.headers["Content-Length"])
554560
assert response_length == min(initial_chunk_size, ret_length)
555561
self.seek(-response_length, io.SEEK_END)
@@ -600,36 +606,43 @@ def _try_initial_chunk_request(
600606

601607
self._request_count += 1
602608
tail = self._session.get(self._url, headers=headers, stream=True)
603-
tail.raise_for_status()
604-
605-
code = tail.status_code
606-
if code != codes.partial_content:
607-
# According to
608-
# https://developer.mozilla.org/en-US/docs/Web/HTTP/Range_requests,
609-
# a 200 OK implies that range requests are not supported,
610-
# regardless of the requested size.
611-
# However, some servers that support negative range requests also return a
612-
# 200 OK if the requested range from the end was larger than the file size.
613-
if code == codes.ok:
614-
accept_ranges = tail.headers.get("Accept-Ranges", None)
615-
content_length = int(tail.headers["Content-Length"])
616-
if accept_ranges == "bytes" and content_length <= initial_chunk_size:
617-
return content_length, tail
609+
try:
610+
tail.raise_for_status()
611+
612+
code = tail.status_code
613+
if code != codes.partial_content:
614+
# According to
615+
# https://developer.mozilla.org/en-US/docs/Web/HTTP/Range_requests,
616+
# a 200 OK implies that range requests are not supported,
617+
# regardless of the requested size.
618+
# However, some servers that support negative range requests also return a
619+
# 200 OK if the requested range from the end was larger than the file size.
620+
if code == codes.ok:
621+
accept_ranges = tail.headers.get("Accept-Ranges", None)
622+
content_length = int(tail.headers["Content-Length"])
623+
if (
624+
accept_ranges == "bytes"
625+
and content_length <= initial_chunk_size
626+
):
627+
return content_length, tail
628+
629+
raise HTTPRangeRequestUnsupportedError(
630+
f"did not receive partial content: got code {code}"
631+
)
618632

619-
raise HTTPRangeRequestUnsupportedError(
620-
f"did not receive partial content: got code {code}"
621-
)
633+
if "Content-Range" not in tail.headers:
634+
raise LazyWheelUnsupportedError(
635+
f"file length cannot be determined for {self._url}, "
636+
f"did not receive content range header from server"
637+
)
622638

623-
if "Content-Range" not in tail.headers:
624-
raise LazyWheelUnsupportedError(
625-
f"file length cannot be determined for {self._url}, "
626-
f"did not receive content range header from server"
639+
file_length = self._parse_full_length_from_content_range(
640+
tail.headers["Content-Range"]
627641
)
628-
629-
file_length = self._parse_full_length_from_content_range(
630-
tail.headers["Content-Range"]
631-
)
632-
return (file_length, tail)
642+
return (file_length, tail)
643+
except BaseException:
644+
tail.close()
645+
raise
633646

634647
def _extract_content_length(
635648
self, initial_chunk_size: int
@@ -683,6 +696,7 @@ def _extract_content_length(
683696
if int(tail.headers["Content-Length"]) > initial_chunk_size or tail.headers.get(
684697
"Content-Range", ""
685698
).startswith("bytes -"):
699+
tail.close()
686700
tail = None
687701
self._domains_without_negative_range.add(domain)
688702
return file_length, tail

src/poetry/utils/helpers.py

Lines changed: 11 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -196,20 +196,26 @@ def _get(self, start: int = 0) -> Response:
196196
headers = {"Accept-Encoding": "Identity"}
197197
if start > 0:
198198
headers["Range"] = f"bytes={start}-"
199+
199200
response = self._session.get(
200201
self._url, stream=True, headers=headers, timeout=REQUESTS_TIMEOUT
201202
)
202-
response.raise_for_status()
203-
return response
203+
try:
204+
response.raise_for_status()
205+
return response
206+
except BaseException:
207+
response.close()
208+
raise
204209

205210
def _iter_content_with_resume(self, chunk_size: int) -> Iterator[bytes]:
206211
fetched_size = 0
207212
retries = 0
208213
while True:
209214
try:
210-
for chunk in self._response.iter_content(chunk_size=chunk_size):
211-
yield chunk
212-
fetched_size += len(chunk)
215+
with self._response:
216+
for chunk in self._response.iter_content(chunk_size=chunk_size):
217+
yield chunk
218+
fetched_size += len(chunk)
213219
except (ChunkedEncodingError, ConnectionError):
214220
if (
215221
retries < self._max_retries

0 commit comments

Comments
 (0)