[Python-checkins] r83122 - in python/branches/release27-maint: Lib/httplib.py Lib/test/test_httplib.py Misc/NEWS

victor.stinner python-checkins at python.org
Sat Jul 24 04:46:16 CEST 2010


Author: victor.stinner
Date: Sat Jul 24 04:46:16 2010
New Revision: 83122

Log:
Forward port r70643 (#5542) + part of r83120 (just remove the comment)

Remove special logic that closes HTTPConnection socket on EPIPE.

If the socket is closed, the client has no chance to read the response
from the server.  EPIPE means that it isn't possible to write more
data from the socket, but not that it is impossible to read.


Modified:
   python/branches/release27-maint/Lib/httplib.py
   python/branches/release27-maint/Lib/test/test_httplib.py
   python/branches/release27-maint/Misc/NEWS

Modified: python/branches/release27-maint/Lib/httplib.py
==============================================================================
--- python/branches/release27-maint/Lib/httplib.py	(original)
+++ python/branches/release27-maint/Lib/httplib.py	Sat Jul 24 04:46:16 2010
@@ -67,6 +67,7 @@
 """
 
 from array import array
+import os
 import socket
 from sys import py3kwarning
 from urlparse import urlsplit
@@ -756,27 +757,17 @@
             else:
                 raise NotConnected()
 
-        # send the data to the server. if we get a broken pipe, then close
-        # the socket. we want to reconnect when somebody tries to send again.
-        #
-        # NOTE: we DO propagate the error, though, because we cannot simply
-        #       ignore the error... the caller will know if they can retry.
         if self.debuglevel > 0:
             print "send:", repr(str)
-        try:
-            blocksize=8192
-            if hasattr(str,'read') and not isinstance(str, array):
-                if self.debuglevel > 0: print "sendIng a read()able"
-                data=str.read(blocksize)
-                while data:
-                    self.sock.sendall(data)
-                    data=str.read(blocksize)
-            else:
-                self.sock.sendall(str)
-        except socket.error, v:
-            if v.args[0] == 32:      # Broken pipe
-                self.close()
-            raise
+        blocksize = 8192
+        if hasattr(str,'read') and not isinstance(str, array):
+            if self.debuglevel > 0: print "sendIng a read()able"
+            data = str.read(blocksize)
+            while data:
+                self.sock.sendall(data)
+                data = str.read(blocksize)
+        else:
+            self.sock.sendall(str)
 
     def _output(self, s):
         """Add a line of output to the current request buffer.
@@ -941,15 +932,7 @@
 
     def request(self, method, url, body=None, headers={}):
         """Send a complete request to the server."""
-
-        try:
-            self._send_request(method, url, body, headers)
-        except socket.error, v:
-            # trap 'Broken pipe' if we're allowed to automatically reconnect
-            if v.args[0] != 32 or not self.auto_open:
-                raise
-            # try one more time
-            self._send_request(method, url, body, headers)
+        self._send_request(method, url, body, headers)
 
     def _set_content_length(self, body):
         # Set the content-length based on the body.
@@ -959,7 +942,6 @@
         except TypeError, te:
             # If this is a file-like object, try to
             # fstat its file descriptor
-            import os
             try:
                 thelen = str(os.fstat(body.fileno()).st_size)
             except (AttributeError, OSError):
@@ -970,7 +952,7 @@
             self.putheader('Content-Length', thelen)
 
     def _send_request(self, method, url, body, headers):
-        # honour explicitly requested Host: and Accept-Encoding headers
+        # Honor explicitly requested Host: and Accept-Encoding: headers.
         header_names = dict.fromkeys([k.lower() for k in headers])
         skips = {}
         if 'host' in header_names:

Modified: python/branches/release27-maint/Lib/test/test_httplib.py
==============================================================================
--- python/branches/release27-maint/Lib/test/test_httplib.py	(original)
+++ python/branches/release27-maint/Lib/test/test_httplib.py	Sat Jul 24 04:46:16 2010
@@ -2,6 +2,7 @@
 import httplib
 import StringIO
 import socket
+import errno
 
 import unittest
 TestCase = unittest.TestCase
@@ -24,6 +25,21 @@
             raise httplib.UnimplementedFileMode()
         return self.fileclass(self.text)
 
+class EPipeSocket(FakeSocket):
+
+    def __init__(self, text, pipe_trigger):
+        # When sendall() is called with pipe_trigger, raise EPIPE.
+        FakeSocket.__init__(self, text)
+        self.pipe_trigger = pipe_trigger
+
+    def sendall(self, data):
+        if self.pipe_trigger in data:
+            raise socket.error(errno.EPIPE, "gotcha")
+        self.data += data
+
+    def close(self):
+        pass
+
 class NoEOFStringIO(StringIO.StringIO):
     """Like StringIO, but raises AssertionError on EOF.
 
@@ -254,6 +270,21 @@
         finally:
             resp.close()
 
+    def test_epipe(self):
+        sock = EPipeSocket(
+            "HTTP/1.0 401 Authorization Required\r\n"
+            "Content-type: text/html\r\n"
+            "WWW-Authenticate: Basic realm=\"example\"\r\n",
+            b"Content-Length")
+        conn = httplib.HTTPConnection("example.com")
+        conn.sock = sock
+        self.assertRaises(socket.error,
+                          lambda: conn.request("PUT", "/url", "body"))
+        resp = conn.getresponse()
+        self.assertEqual(401, resp.status)
+        self.assertEqual("Basic realm=\"example\"",
+                         resp.getheader("www-authenticate"))
+
 
 class OfflineTest(TestCase):
     def test_responses(self):

Modified: python/branches/release27-maint/Misc/NEWS
==============================================================================
--- python/branches/release27-maint/Misc/NEWS	(original)
+++ python/branches/release27-maint/Misc/NEWS	Sat Jul 24 04:46:16 2010
@@ -18,6 +18,8 @@
 Library
 -------
 
+- Issue #5542: Remove special logic that closes HTTPConnection socket on EPIPE.
+
 - Issue #4629: getopt raises an error if an argument ends with = whereas getopt
   doesn't except a value (eg. --help= is rejected if getopt uses ['help='] long
   options).


More information about the Python-checkins mailing list