Index: Lib/ftplib.py =================================================================== --- Lib/ftplib.py (revision 79421) +++ Lib/ftplib.py (working copy) @@ -582,6 +582,26 @@ except ImportError: pass else: + # SSL helper to unwrap the connection reliably. Issue 8108. + # See also: http://marc.info/?l=openssl-users&m=126838806919896 + def _ssl_unwrap(conn): + count = 0 + while True: + try: + conn.unwrap() + except ssl.SSLError, err: + if err.args[0] not in (ssl.SSL_ERROR_WANT_READ, + ssl.SSL_ERROR_WANT_WRITE): + raise + assert err.args[0] == ssl.SSL_ERROR_WANT_READ + count += 1 + else: + break + if count: + print >>sys.stderr, count, 'ssl want read' + else: + print >>sys.stderr, 'success' + class FTP_TLS(FTP): '''A FTP subclass which adds TLS support to FTP as described in RFC-4217. @@ -684,7 +704,7 @@ callback(data) # shutdown ssl layer if isinstance(conn, ssl.SSLSocket): - conn.unwrap() + _ssl_unwrap(conn) finally: conn.close() return self.voidresp() @@ -707,7 +727,7 @@ callback(line) # shutdown ssl layer if isinstance(conn, ssl.SSLSocket): - conn.unwrap() + _ssl_unwrap(conn) finally: fp.close() conn.close() @@ -724,7 +744,7 @@ if callback: callback(buf) # shutdown ssl layer if isinstance(conn, ssl.SSLSocket): - conn.unwrap() + _ssl_unwrap(conn) finally: conn.close() return self.voidresp() @@ -743,7 +763,7 @@ if callback: callback(buf) # shutdown ssl layer if isinstance(conn, ssl.SSLSocket): - conn.unwrap() + _ssl_unwrap(conn) finally: conn.close() return self.voidresp() Index: Lib/test/test_ftplib.py =================================================================== --- Lib/test/test_ftplib.py (revision 79421) +++ Lib/test/test_ftplib.py (working copy) @@ -318,7 +318,18 @@ try: if isinstance(self.socket, ssl.SSLSocket): if self.socket._sslobj is not None: - self.socket.unwrap() + # Unwrap the connection reliably. Issue 8108. + # http://marc.info/?l=openssl-users&m=126838806919896 + try: + self.socket.unwrap() + except ssl.SSLError, err: + if err.args[0] not in (ssl.SSL_ERROR_WANT_READ, + ssl.SSL_ERROR_WANT_WRITE): + raise + except socket.error, err: + # remote site dropped the connection + if err.args[0] not in (errno.EPIPE, 0): + raise finally: super(SSLConnection, self).close()