From: Antoine Pitrou Date: Sat, 15 Dec 2012 18:22:30 +0000 (+0100) Subject: Issue #16298: In HTTPResponse.read(), close the socket when there is no Content-Lengt... X-Git-Tag: v3.3.1rc1~530 X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=d20e7745eeb99c5f2089b5b6ffa351658cb4e839;p=python Issue #16298: In HTTPResponse.read(), close the socket when there is no Content-Length and the incoming stream is finished. Patch by Eran Rundstein. --- d20e7745eeb99c5f2089b5b6ffa351658cb4e839 diff --cc Lib/http/client.py index 9b01704eb0,4d93b93ff0..6a4496fb49 --- a/Lib/http/client.py +++ b/Lib/http/client.py @@@ -531,44 -506,18 +531,47 @@@ class HTTPResponse(io.RawIOBase) # we do not use _safe_read() here because this may be a .will_close # connection, and the user is reading more bytes than will be provided # (for example, reading in 1k chunks) - s = self.fp.read(amt) + n = self.fp.readinto(b) if self.length is not None: - self.length -= len(s) + self.length -= n if not self.length: self.close() + else: - if not s: ++ if not n: + self.close() + return n - return s + def _read_next_chunk_size(self): + # Read the next chunk size from the file + line = self.fp.readline(_MAXLINE + 1) + if len(line) > _MAXLINE: + raise LineTooLong("chunk size") + i = line.find(b";") + if i >= 0: + line = line[:i] # strip chunk-extensions + try: + return int(line, 16) + except ValueError: + # close the connection as protocol synchronisation is + # probably lost + self.close() + raise - def _read_chunked(self, amt): + def _read_and_discard_trailer(self): + # read and discard trailer up to the CRLF terminator + ### note: we shouldn't have any trailers! + while True: + line = self.fp.readline(_MAXLINE + 1) + if len(line) > _MAXLINE: + raise LineTooLong("trailer line") + if not line: + # a vanishingly small number of sites EOF without + # sending the trailer + break + if line in (b'\r\n', b'\n', b''): + break + + def _readall_chunked(self): assert self.chunked != _UNKNOWN chunk_left = self.chunk_left value = [] diff --cc Lib/test/test_httplib.py index cf61552da1,b0777d4286..5ebcfcb939 --- a/Lib/test/test_httplib.py +++ b/Lib/test/test_httplib.py @@@ -186,23 -186,19 +186,55 @@@ class BasicTest(TestCase) self.assertEqual(resp.read(2), b'xt') self.assertTrue(resp.isclosed()) + def test_partial_readintos(self): - # if we have a lenght, the system knows when to close itself ++ # if we have a length, the system knows when to close itself + # same behaviour than when we read the whole thing with read() + body = "HTTP/1.1 200 Ok\r\nContent-Length: 4\r\n\r\nText" + sock = FakeSocket(body) + resp = client.HTTPResponse(sock) + resp.begin() + b = bytearray(2) + n = resp.readinto(b) + self.assertEqual(n, 2) + self.assertEqual(bytes(b), b'Te') + self.assertFalse(resp.isclosed()) + n = resp.readinto(b) + self.assertEqual(n, 2) + self.assertEqual(bytes(b), b'xt') + self.assertTrue(resp.isclosed()) + + def test_partial_reads_no_content_length(self): + # when no length is present, the socket should be gracefully closed when + # all data was read + body = "HTTP/1.1 200 Ok\r\n\r\nText" + sock = FakeSocket(body) + resp = client.HTTPResponse(sock) + resp.begin() + self.assertEqual(resp.read(2), b'Te') + self.assertFalse(resp.isclosed()) + self.assertEqual(resp.read(2), b'xt') + self.assertEqual(resp.read(1), b'') + self.assertTrue(resp.isclosed()) + ++ def test_partial_readintos_no_content_length(self): ++ # when no length is present, the socket should be gracefully closed when ++ # all data was read ++ body = "HTTP/1.1 200 Ok\r\n\r\nText" ++ sock = FakeSocket(body) ++ resp = client.HTTPResponse(sock) ++ resp.begin() ++ b = bytearray(2) ++ n = resp.readinto(b) ++ self.assertEqual(n, 2) ++ self.assertEqual(bytes(b), b'Te') ++ self.assertFalse(resp.isclosed()) ++ n = resp.readinto(b) ++ self.assertEqual(n, 2) ++ self.assertEqual(bytes(b), b'xt') ++ n = resp.readinto(b) ++ self.assertEqual(n, 0) ++ self.assertTrue(resp.isclosed()) ++ def test_host_port(self): # Check invalid host_port diff --cc Misc/NEWS index 604e6fa8ce,57cab2d77e..5020c9d42d --- a/Misc/NEWS +++ b/Misc/NEWS @@@ -108,10 -179,10 +108,14 @@@ Core and Builtin Library ------- + - Issue #16298: In HTTPResponse.read(), close the socket when there is no + Content-Length and the incoming stream is finished. Patch by Eran + Rundstein. + +- Issue #15872: Fix 3.3 regression introduced by the new fd-based shutil.rmtree + that caused it to not ignore certain errors when ignore_errors was set. + Patch by Alessandro Moura and Serhiy Storchaka. + - Issue #16248: Disable code execution from the user's home directory by tkinter when the -E flag is passed to Python. Patch by Zachary Ware.