]> granicus.if.org Git - python/commitdiff
Issue #16298: In HTTPResponse.read(), close the socket when there is no Content-Lengt...
authorAntoine Pitrou <solipsis@pitrou.net>
Sat, 15 Dec 2012 18:22:30 +0000 (19:22 +0100)
committerAntoine Pitrou <solipsis@pitrou.net>
Sat, 15 Dec 2012 18:22:30 +0000 (19:22 +0100)
Patch by Eran Rundstein.

1  2 
Lib/http/client.py
Lib/test/test_httplib.py
Misc/ACKS
Misc/NEWS

index 9b01704eb01f764dd8d2824ddaa38857d1255bf1,4d93b93ff012d460b9ebab87ad13d17e23dbb1fe..6a4496fb49e35a91b3389268a2a78e524f0f456a
@@@ -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()
 -            if not s:
+         else:
++            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 = []
index cf61552da1ac406788c16dc3e5770cac6b8db170,b0777d42867c4f88dd644f17f61c43f0a5dc9150..5ebcfcb939d6b8ebb8c647a980fc04b7013dd2cd
@@@ -186,23 -186,19 +186,55 @@@ class BasicTest(TestCase)
          self.assertEqual(resp.read(2), b'xt')
          self.assertTrue(resp.isclosed())
  
-         # if we have a lenght, the system knows when to close itself
 +    def test_partial_readintos(self):
++        # 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/ACKS
Simple merge
diff --cc Misc/NEWS
index 604e6fa8cee612c907c812b03f8a83de78fb99ad,57cab2d77e566bb4afcb63dfd09375fd8e5bfbdc..5020c9d42d18c7b97636791a676a1de4fe8c4dba
+++ 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.