[Python-checkins] python/dist/src/Lib httplib.py,1.57,1.58

jhylton@users.sourceforge.net jhylton@users.sourceforge.net
Tue, 09 Jul 2002 14:22:38 -0700


Update of /cvsroot/python/python/dist/src/Lib
In directory usw-pr-cvs1:/tmp/cvs-serv26945

Modified Files:
	httplib.py 
Log Message:
Fix for SF bug 579107.

The recent SSL changes resulted in important, but subtle changes to
close() semantics.  Since builtin socket makefile() is not called for
SSL connections, we don't get separately closeable fds for connection
and response.  Comments in the code explain how to restore makefile
semantics.

Bug fix candidate.


Index: httplib.py
===================================================================
RCS file: /cvsroot/python/python/dist/src/Lib/httplib.py,v
retrieving revision 1.57
retrieving revision 1.58
diff -C2 -d -r1.57 -r1.58
*** httplib.py	7 Jul 2002 16:51:37 -0000	1.57
--- httplib.py	9 Jul 2002 21:22:36 -0000	1.58
***************
*** 755,759 ****
          return response
  
! class SSLFile:
      """File-like object wrapping an SSL socket."""
  
--- 755,805 ----
          return response
  
! # The next several classes are used to define FakeSocket,a socket-like
! # interface to an SSL connection.
! 
! # The primary complexity comes from faking a makefile() method.  The
! # standard socket makefile() implementation calls dup() on the socket
! # file descriptor.  As a consequence, clients can call close() on the
! # parent socket and its makefile children in any order.  The underlying
! # socket isn't closed until they are all closed.
! 
! # The implementation uses reference counting to keep the socket open
! # until the last client calls close().  SharedSocket keeps track of
! # the reference counting and SharedSocketClient provides an constructor
! # and close() method that call incref() and decref() correctly.
! 
! class SharedSocket:
! 
!     def __init__(self, sock):
!         self.sock = sock
!         self._refcnt = 0
! 
!     def incref(self):
!         self._refcnt += 1
! 
!     def decref(self):
!         self._refcnt -= 1
!         assert self._refcnt >= 0
!         if self._refcnt == 0:
!             self.sock.close()
! 
!     def __del__(self):
!         self.sock.close()
! 
! class SharedSocketClient:
! 
!     def __init__(self, shared):
!         self._closed = 0
!         self._shared = shared
!         self._shared.incref()
!         self._sock = shared.sock
! 
!     def close(self):
!         if not self._closed:
!             self._shared.decref()
!             self._closed = 1
!             self._shared = None
! 
! class SSLFile(SharedSocketClient):
      """File-like object wrapping an SSL socket."""
  
***************
*** 761,765 ****
      
      def __init__(self, sock, ssl, bufsize=None):
!         self._sock = sock
          self._ssl = ssl
          self._buf = ''
--- 807,811 ----
      
      def __init__(self, sock, ssl, bufsize=None):
!         SharedSocketClient.__init__(self, sock)
          self._ssl = ssl
          self._buf = ''
***************
*** 830,857 ****
              return line
  
!     def close(self):
!         self._sock.close()
  
- class FakeSocket:
      def __init__(self, sock, ssl):
!         self.__sock = sock
!         self.__ssl = ssl
  
      def makefile(self, mode, bufsize=None):
          if mode != 'r' and mode != 'rb':
              raise UnimplementedFileMode()
!         return SSLFile(self.__sock, self.__ssl, bufsize)
  
      def send(self, stuff, flags = 0):
!         return self.__ssl.write(stuff)
  
!     def sendall(self, stuff, flags = 0):
!         return self.__ssl.write(stuff)
  
      def recv(self, len = 1024, flags = 0):
!         return self.__ssl.read(len)
  
      def __getattr__(self, attr):
!         return getattr(self.__sock, attr)
  
  
--- 876,909 ----
              return line
  
! class FakeSocket(SharedSocketClient):
! 
!     class _closedsocket:
!         def __getattr__(self, name):
!             raise error(9, 'Bad file descriptor')
  
      def __init__(self, sock, ssl):
!         sock = SharedSocket(sock)
!         SharedSocketClient.__init__(self, sock)
!         self._ssl = ssl
! 
!     def close(self):
!         SharedSocketClient.close(self)
!         self._sock = self.__class__._closedsocket()
  
      def makefile(self, mode, bufsize=None):
          if mode != 'r' and mode != 'rb':
              raise UnimplementedFileMode()
!         return SSLFile(self._shared, self._ssl, bufsize)
  
      def send(self, stuff, flags = 0):
!         return self._ssl.write(stuff)
  
!     sendall = send
  
      def recv(self, len = 1024, flags = 0):
!         return self._ssl.read(len)
  
      def __getattr__(self, attr):
!         return getattr(self._sock, attr)
  
  
***************
*** 1102,1114 ****
              return L + self._file.readlines(size)
  
- #
- # snarfed from httplib.py for now...
- #
  def test():
      """Test this module.
  
!     The test consists of retrieving and displaying the Python
!     home page, along with the error code and error string returned
!     by the www.python.org server.
      """
  
--- 1154,1162 ----
              return L + self._file.readlines(size)
  
  def test():
      """Test this module.
  
!     A hodge podge of tests collected here, because they have too many
!     external dependencies for the regular test suite.
      """
  
***************
*** 1131,1139 ****
      print 'status =', status
      print 'reason =', reason
      print
      if headers:
          for header in headers.headers: print header.strip()
      print
-     print "read", len(h.getfile().read())
  
      # minimal test that code to extract host from url works
--- 1179,1187 ----
      print 'status =', status
      print 'reason =', reason
+     print "read", len(h.getfile().read())
      print
      if headers:
          for header in headers.headers: print header.strip()
      print
  
      # minimal test that code to extract host from url works
***************
*** 1149,1168 ****
  
      if hasattr(socket, 'ssl'):
!         host = 'sourceforge.net'
!         selector = '/projects/python'
!         hs = HTTPS()
!         hs.connect(host)
!         hs.putrequest('GET', selector)
!         hs.endheaders()
!         status, reason, headers = hs.getreply()
!         # XXX why does this give a 302 response?
!         print 'status =', status
!         print 'reason =', reason
!         print
!         if headers:
!             for header in headers.headers: print header.strip()
!         print
!         print "read", len(hs.getfile().read())
  
  
      # Test a buggy server -- returns garbled status line.
--- 1197,1220 ----
  
      if hasattr(socket, 'ssl'):
!         
!         for host, selector in (('sourceforge.net', '/projects/python'),
!                                ('dbserv2.theopalgroup.com', '/mediumfile'),
!                                ('dbserv2.theopalgroup.com', '/smallfile'),
!                                ):
!             print "https://%s%s" % (host, selector)
!             hs = HTTPS()
!             hs.connect(host)
!             hs.putrequest('GET', selector)
!             hs.endheaders()
!             status, reason, headers = hs.getreply()
!             print 'status =', status
!             print 'reason =', reason
!             print "read", len(hs.getfile().read())
!             print
!             if headers:
!                 for header in headers.headers: print header.strip()
!             print
  
+     return
  
      # Test a buggy server -- returns garbled status line.