Skip to content

gh-113199: Make read1 and readline of HTTPResponse close IO after reading all data #113200

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 4 commits into from
Dec 18, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions Lib/http/client.py
Original file line number Diff line number Diff line change
Expand Up @@ -665,6 +665,8 @@ def read1(self, n=-1):
self._close_conn()
elif self.length is not None:
self.length -= len(result)
if not self.length:
self._close_conn()
return result

def peek(self, n=-1):
Expand All @@ -689,6 +691,8 @@ def readline(self, limit=-1):
self._close_conn()
elif self.length is not None:
self.length -= len(result)
if not self.length:
self._close_conn()
return result

def _read1_chunked(self, n):
Expand Down
16 changes: 14 additions & 2 deletions Lib/test/test_httplib.py
Original file line number Diff line number Diff line change
Expand Up @@ -1546,18 +1546,22 @@ def test_readline(self):
resp = self.resp
self._verify_readline(self.resp.readline, self.lines_expected)

def _verify_readline(self, readline, expected):
def test_readline_without_limit(self):
self._verify_readline(self.resp.readline, self.lines_expected, limit=-1)

def _verify_readline(self, readline, expected, limit=5):
all = []
while True:
# short readlines
line = readline(5)
line = readline(limit)
if line and line != b"foo":
if len(line) < 5:
self.assertTrue(line.endswith(b"\n"))
all.append(line)
if not line:
break
self.assertEqual(b"".join(all), expected)
self.assertTrue(self.resp.isclosed())

def test_read1(self):
resp = self.resp
Expand All @@ -1577,6 +1581,7 @@ def test_read1_unbounded(self):
break
all.append(data)
self.assertEqual(b"".join(all), self.lines_expected)
self.assertTrue(resp.isclosed())

def test_read1_bounded(self):
resp = self.resp
Expand All @@ -1588,15 +1593,22 @@ def test_read1_bounded(self):
self.assertLessEqual(len(data), 10)
all.append(data)
self.assertEqual(b"".join(all), self.lines_expected)
self.assertTrue(resp.isclosed())

def test_read1_0(self):
self.assertEqual(self.resp.read1(0), b"")
self.assertFalse(self.resp.isclosed())

def test_peek_0(self):
p = self.resp.peek(0)
self.assertLessEqual(0, len(p))


class ExtendedReadTestContentLengthKnown(ExtendedReadTest):
_header, _body = ExtendedReadTest.lines.split('\r\n\r\n', 1)
lines = _header + f'\r\nContent-Length: {len(_body)}\r\n\r\n' + _body


class ExtendedReadTestChunked(ExtendedReadTest):
"""
Test peek(), read1(), readline() in chunked mode
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
Make ``http.client.HTTPResponse.read1`` and
``http.client.HTTPResponse.readline`` close IO after reading all data when
content length is known. Patch by Illia Volochii.