From b5b9c8cd402b8ab8d6cb00f157ac855b37a1e5c6 Mon Sep 17 00:00:00 2001 From: Serhiy Storchaka Date: Wed, 6 Feb 2013 10:31:57 +0200 Subject: Issue #16723: httplib.HTTPResponse no longer marked closed when the connection is automatically closed. --- Lib/http/client.py | 34 +++++++++++++++++++--------------- Lib/test/test_httplib.py | 18 ++++++++++++++++++ Misc/NEWS | 2 ++ 3 files changed, 39 insertions(+), 15 deletions(-) diff --git a/Lib/http/client.py b/Lib/http/client.py index 4574eed..5466d06 100644 --- a/Lib/http/client.py +++ b/Lib/http/client.py @@ -324,7 +324,7 @@ class HTTPResponse(io.RawIOBase): # empty version will cause next test to fail. version = "" if not version.startswith("HTTP/"): - self.close() + self._close_conn() raise BadStatusLine(line) # The status code is a three-digit number @@ -446,22 +446,25 @@ class HTTPResponse(io.RawIOBase): # otherwise, assume it will close return True + def _close_conn(self): + fp = self.fp + self.fp = None + fp.close() + def close(self): + super().close() # set "closed" flag if self.fp: - self.fp.close() - self.fp = None + self._close_conn() # These implementations are for the benefit of io.BufferedReader. # XXX This class should probably be revised to act more like # the "raw stream" that BufferedReader expects. - @property - def closed(self): - return self.isclosed() - def flush(self): - self.fp.flush() + super().flush() + if self.fp: + self.fp.flush() def readable(self): return True @@ -469,6 +472,7 @@ class HTTPResponse(io.RawIOBase): # End of "raw stream" methods def isclosed(self): + """True if the connection is closed.""" # NOTE: it is possible that we will not ever call self.close(). This # case occurs when will_close is TRUE, length is None, and we # read up to the last byte, but NOT past it. @@ -482,7 +486,7 @@ class HTTPResponse(io.RawIOBase): return b"" if self._method == "HEAD": - self.close() + self._close_conn() return b"" if self.chunked: @@ -496,10 +500,10 @@ class HTTPResponse(io.RawIOBase): try: s = self._safe_read(self.length) except IncompleteRead: - self.close() + self._close_conn() raise self.length = 0 - self.close() # we read everything + self._close_conn() # we read everything return s if self.length is not None: @@ -514,11 +518,11 @@ class HTTPResponse(io.RawIOBase): if not s: # Ideally, we would raise IncompleteRead if the content-length # wasn't satisfied, but it might break compatibility. - self.close() + self._close_conn() elif self.length is not None: self.length -= len(s) if not self.length: - self.close() + self._close_conn() return s @@ -539,7 +543,7 @@ class HTTPResponse(io.RawIOBase): except ValueError: # close the connection as protocol synchronisation is # probably lost - self.close() + self._close_conn() raise IncompleteRead(b''.join(value)) if chunk_left == 0: break @@ -576,7 +580,7 @@ class HTTPResponse(io.RawIOBase): break # we read everything; close the "file" - self.close() + self._close_conn() return b''.join(value) diff --git a/Lib/test/test_httplib.py b/Lib/test/test_httplib.py index 5df4b51..420302c 100644 --- a/Lib/test/test_httplib.py +++ b/Lib/test/test_httplib.py @@ -164,6 +164,9 @@ class BasicTest(TestCase): resp.begin() self.assertEqual(resp.read(), b"Text") self.assertTrue(resp.isclosed()) + self.assertFalse(resp.closed) + resp.close() + self.assertTrue(resp.closed) body = "HTTP/1.1 400.100 Not Ok\r\n\r\nText" sock = FakeSocket(body) @@ -185,6 +188,9 @@ class BasicTest(TestCase): self.assertFalse(resp.isclosed()) self.assertEqual(resp.read(2), b'xt') self.assertTrue(resp.isclosed()) + self.assertFalse(resp.closed) + resp.close() + self.assertTrue(resp.closed) def test_partial_reads_no_content_length(self): # when no length is present, the socket should be gracefully closed when @@ -198,6 +204,9 @@ class BasicTest(TestCase): self.assertEqual(resp.read(2), b'xt') self.assertEqual(resp.read(1), b'') self.assertTrue(resp.isclosed()) + self.assertFalse(resp.closed) + resp.close() + self.assertTrue(resp.closed) def test_partial_reads_incomplete_body(self): # if the server shuts down the connection before the whole @@ -211,6 +220,9 @@ class BasicTest(TestCase): self.assertEqual(resp.read(2), b'xt') self.assertEqual(resp.read(1), b'') self.assertTrue(resp.isclosed()) + self.assertFalse(resp.closed) + resp.close() + self.assertTrue(resp.closed) def test_host_port(self): # Check invalid host_port @@ -355,6 +367,9 @@ class BasicTest(TestCase): self.assertEqual(resp.status, 200) self.assertEqual(resp.reason, 'OK') self.assertTrue(resp.isclosed()) + self.assertFalse(resp.closed) + resp.close() + self.assertTrue(resp.closed) def test_negative_content_length(self): sock = FakeSocket( @@ -430,6 +445,9 @@ class BasicTest(TestCase): resp.begin() self.assertEqual(resp.read(), b'') self.assertTrue(resp.isclosed()) + self.assertFalse(resp.closed) + resp.close() + self.assertTrue(resp.closed) class OfflineTest(TestCase): def test_responses(self): diff --git a/Misc/NEWS b/Misc/NEWS index b7eabd7..78e1c85 100644 --- a/Misc/NEWS +++ b/Misc/NEWS @@ -212,6 +212,8 @@ Core and Builtins Library ------- +- Issue #16723: httplib.HTTPResponse no longer marked closed when the connection + is automatically closed. - Issue #16948: Fix quoted printable body encoding for non-latin1 character sets in the email package. -- cgit v0.12