From 73222213787d86d900d4dfab2c2cf9cb41b9bf77 Mon Sep 17 00:00:00 2001 From: Illia Volochii Date: Sat, 16 Dec 2023 01:13:05 +0200 Subject: [PATCH 1/3] gh-113199: Make `HTTPResponse.read1` close IO after reading all data --- Lib/http/client.py | 2 ++ Lib/test/test_httplib.py | 8 ++++++++ .../2023-12-16-01-10-47.gh-issue-113199.oDjnjL.rst | 2 ++ 3 files changed, 12 insertions(+) create mode 100644 Misc/NEWS.d/next/Library/2023-12-16-01-10-47.gh-issue-113199.oDjnjL.rst diff --git a/Lib/http/client.py b/Lib/http/client.py index 7bb5d824bb6da4..a81c80b5f855c7 100644 --- a/Lib/http/client.py +++ b/Lib/http/client.py @@ -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): diff --git a/Lib/test/test_httplib.py b/Lib/test/test_httplib.py index caa4c76a913a01..b9bbdaa99ce01b 100644 --- a/Lib/test/test_httplib.py +++ b/Lib/test/test_httplib.py @@ -1577,6 +1577,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 @@ -1588,15 +1589,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'Content-Length: {len(_body)}\r\n\r\n' + _body + + class ExtendedReadTestChunked(ExtendedReadTest): """ Test peek(), read1(), readline() in chunked mode diff --git a/Misc/NEWS.d/next/Library/2023-12-16-01-10-47.gh-issue-113199.oDjnjL.rst b/Misc/NEWS.d/next/Library/2023-12-16-01-10-47.gh-issue-113199.oDjnjL.rst new file mode 100644 index 00000000000000..cc9c3abe5cf1b9 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2023-12-16-01-10-47.gh-issue-113199.oDjnjL.rst @@ -0,0 +1,2 @@ +Make ``http.client.HTTPResponse.read1`` close IO after reading all data when +content length is known. Patch by Illia Volochii. From 70d4c3784200800ca444e8b17ad90c34f74ab2c5 Mon Sep 17 00:00:00 2001 From: Illia Volochii Date: Sun, 17 Dec 2023 18:44:25 +0200 Subject: [PATCH 2/3] Add a missing newline --- Lib/test/test_httplib.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Lib/test/test_httplib.py b/Lib/test/test_httplib.py index b9bbdaa99ce01b..5b9772ef7bb9ce 100644 --- a/Lib/test/test_httplib.py +++ b/Lib/test/test_httplib.py @@ -1602,7 +1602,7 @@ def test_peek_0(self): class ExtendedReadTestContentLengthKnown(ExtendedReadTest): _header, _body = ExtendedReadTest.lines.split('\r\n\r\n', 1) - lines = _header + f'Content-Length: {len(_body)}\r\n\r\n' + _body + lines = _header + f'\r\nContent-Length: {len(_body)}\r\n\r\n' + _body class ExtendedReadTestChunked(ExtendedReadTest): From b4ca2683c5293899bb06f4d18148337eec1fb991 Mon Sep 17 00:00:00 2001 From: Illia Volochii Date: Sun, 17 Dec 2023 18:47:53 +0200 Subject: [PATCH 3/3] Make `HTTPResponse.readline` close IO after reading all data too --- Lib/http/client.py | 2 ++ Lib/test/test_httplib.py | 8 ++++++-- .../2023-12-16-01-10-47.gh-issue-113199.oDjnjL.rst | 3 ++- 3 files changed, 10 insertions(+), 3 deletions(-) diff --git a/Lib/http/client.py b/Lib/http/client.py index a81c80b5f855c7..5eebfccafbca59 100644 --- a/Lib/http/client.py +++ b/Lib/http/client.py @@ -691,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): diff --git a/Lib/test/test_httplib.py b/Lib/test/test_httplib.py index 5b9772ef7bb9ce..089bf5be40a0e2 100644 --- a/Lib/test/test_httplib.py +++ b/Lib/test/test_httplib.py @@ -1546,11 +1546,14 @@ 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")) @@ -1558,6 +1561,7 @@ def _verify_readline(self, readline, expected): if not line: break self.assertEqual(b"".join(all), expected) + self.assertTrue(self.resp.isclosed()) def test_read1(self): resp = self.resp diff --git a/Misc/NEWS.d/next/Library/2023-12-16-01-10-47.gh-issue-113199.oDjnjL.rst b/Misc/NEWS.d/next/Library/2023-12-16-01-10-47.gh-issue-113199.oDjnjL.rst index cc9c3abe5cf1b9..d8e0b1731d1e3b 100644 --- a/Misc/NEWS.d/next/Library/2023-12-16-01-10-47.gh-issue-113199.oDjnjL.rst +++ b/Misc/NEWS.d/next/Library/2023-12-16-01-10-47.gh-issue-113199.oDjnjL.rst @@ -1,2 +1,3 @@ -Make ``http.client.HTTPResponse.read1`` close IO after reading all data when +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.