Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit b6c86fd

Browse files
Issue #16723: httplib.HTTPResponse no longer marked closed when the connection
is automatically closed.
2 parents e201e9d + b5b9c8c commit b6c86fd

3 files changed

Lines changed: 48 additions & 17 deletions

File tree

Lib/http/client.py

Lines changed: 21 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -332,7 +332,7 @@ def _read_status(self):
332332
# empty version will cause next test to fail.
333333
version = ""
334334
if not version.startswith("HTTP/"):
335-
self.close()
335+
self._close_conn()
336336
raise BadStatusLine(line)
337337

338338
# The status code is a three-digit number
@@ -454,29 +454,33 @@ def _check_close(self):
454454
# otherwise, assume it will close
455455
return True
456456

457+
def _close_conn(self):
458+
fp = self.fp
459+
self.fp = None
460+
fp.close()
461+
457462
def close(self):
463+
super().close() # set "closed" flag
458464
if self.fp:
459-
self.fp.close()
460-
self.fp = None
465+
self._close_conn()
461466

462467
# These implementations are for the benefit of io.BufferedReader.
463468

464469
# XXX This class should probably be revised to act more like
465470
# the "raw stream" that BufferedReader expects.
466471

467-
@property
468-
def closed(self):
469-
return self.isclosed()
470-
471472
def flush(self):
472-
self.fp.flush()
473+
super().flush()
474+
if self.fp:
475+
self.fp.flush()
473476

474477
def readable(self):
475478
return True
476479

477480
# End of "raw stream" methods
478481

479482
def isclosed(self):
483+
"""True if the connection is closed."""
480484
# NOTE: it is possible that we will not ever call self.close(). This
481485
# case occurs when will_close is TRUE, length is None, and we
482486
# read up to the last byte, but NOT past it.
@@ -490,7 +494,7 @@ def read(self, amt=None):
490494
return b""
491495

492496
if self._method == "HEAD":
493-
self.close()
497+
self._close_conn()
494498
return b""
495499

496500
if amt is not None:
@@ -510,18 +514,18 @@ def read(self, amt=None):
510514
try:
511515
s = self._safe_read(self.length)
512516
except IncompleteRead:
513-
self.close()
517+
self._close_conn()
514518
raise
515519
self.length = 0
516-
self.close() # we read everything
520+
self._close_conn() # we read everything
517521
return s
518522

519523
def readinto(self, b):
520524
if self.fp is None:
521525
return 0
522526

523527
if self._method == "HEAD":
524-
self.close()
528+
self._close_conn()
525529
return 0
526530

527531
if self.chunked:
@@ -539,11 +543,11 @@ def readinto(self, b):
539543
if not n:
540544
# Ideally, we would raise IncompleteRead if the content-length
541545
# wasn't satisfied, but it might break compatibility.
542-
self.close()
546+
self._close_conn()
543547
elif self.length is not None:
544548
self.length -= n
545549
if not self.length:
546-
self.close()
550+
self._close_conn()
547551
return n
548552

549553
def _read_next_chunk_size(self):
@@ -559,7 +563,7 @@ def _read_next_chunk_size(self):
559563
except ValueError:
560564
# close the connection as protocol synchronisation is
561565
# probably lost
562-
self.close()
566+
self._close_conn()
563567
raise
564568

565569
def _read_and_discard_trailer(self):
@@ -597,7 +601,7 @@ def _readall_chunked(self):
597601
self._read_and_discard_trailer()
598602

599603
# we read everything; close the "file"
600-
self.close()
604+
self._close_conn()
601605

602606
return b''.join(value)
603607

@@ -638,7 +642,7 @@ def _readinto_chunked(self, b):
638642
self._read_and_discard_trailer()
639643

640644
# we read everything; close the "file"
641-
self.close()
645+
self._close_conn()
642646

643647
return total_bytes
644648

Lib/test/test_httplib.py

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -164,6 +164,9 @@ def test_status_lines(self):
164164
resp.begin()
165165
self.assertEqual(resp.read(), b"Text")
166166
self.assertTrue(resp.isclosed())
167+
self.assertFalse(resp.closed)
168+
resp.close()
169+
self.assertTrue(resp.closed)
167170

168171
body = "HTTP/1.1 400.100 Not Ok\r\n\r\nText"
169172
sock = FakeSocket(body)
@@ -185,6 +188,9 @@ def test_partial_reads(self):
185188
self.assertFalse(resp.isclosed())
186189
self.assertEqual(resp.read(2), b'xt')
187190
self.assertTrue(resp.isclosed())
191+
self.assertFalse(resp.closed)
192+
resp.close()
193+
self.assertTrue(resp.closed)
188194

189195
def test_partial_readintos(self):
190196
# if we have a length, the system knows when to close itself
@@ -202,6 +208,9 @@ def test_partial_readintos(self):
202208
self.assertEqual(n, 2)
203209
self.assertEqual(bytes(b), b'xt')
204210
self.assertTrue(resp.isclosed())
211+
self.assertFalse(resp.closed)
212+
resp.close()
213+
self.assertTrue(resp.closed)
205214

206215
def test_partial_reads_no_content_length(self):
207216
# when no length is present, the socket should be gracefully closed when
@@ -215,6 +224,9 @@ def test_partial_reads_no_content_length(self):
215224
self.assertEqual(resp.read(2), b'xt')
216225
self.assertEqual(resp.read(1), b'')
217226
self.assertTrue(resp.isclosed())
227+
self.assertFalse(resp.closed)
228+
resp.close()
229+
self.assertTrue(resp.closed)
218230

219231
def test_partial_readintos_no_content_length(self):
220232
# when no length is present, the socket should be gracefully closed when
@@ -266,6 +278,9 @@ def test_partial_readintos_incomplete_body(self):
266278
n = resp.readinto(b)
267279
self.assertEqual(n, 0)
268280
self.assertTrue(resp.isclosed())
281+
self.assertFalse(resp.closed)
282+
resp.close()
283+
self.assertTrue(resp.closed)
269284

270285
def test_host_port(self):
271286
# Check invalid host_port
@@ -493,6 +508,9 @@ def test_chunked_head(self):
493508
self.assertEqual(resp.status, 200)
494509
self.assertEqual(resp.reason, 'OK')
495510
self.assertTrue(resp.isclosed())
511+
self.assertFalse(resp.closed)
512+
resp.close()
513+
self.assertTrue(resp.closed)
496514

497515
def test_readinto_chunked_head(self):
498516
chunked_start = (
@@ -513,6 +531,9 @@ def test_readinto_chunked_head(self):
513531
self.assertEqual(resp.status, 200)
514532
self.assertEqual(resp.reason, 'OK')
515533
self.assertTrue(resp.isclosed())
534+
self.assertFalse(resp.closed)
535+
resp.close()
536+
self.assertTrue(resp.closed)
516537

517538
def test_negative_content_length(self):
518539
sock = FakeSocket(
@@ -588,6 +609,9 @@ def test_early_eof(self):
588609
resp.begin()
589610
self.assertEqual(resp.read(), b'')
590611
self.assertTrue(resp.isclosed())
612+
self.assertFalse(resp.closed)
613+
resp.close()
614+
self.assertTrue(resp.closed)
591615

592616
class OfflineTest(TestCase):
593617
def test_responses(self):

Misc/NEWS

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -163,6 +163,9 @@ Core and Builtins
163163
Library
164164
-------
165165

166+
- Issue #16723: httplib.HTTPResponse no longer marked closed when the connection
167+
is automatically closed.
168+
166169
- Issue #16948: Fix quoted printable body encoding for non-latin1 character
167170
sets in the email package.
168171

0 commit comments

Comments
 (0)