Index: Lib/ftplib.py =================================================================== --- Lib/ftplib.py (revision 79421) +++ Lib/ftplib.py (working copy) @@ -582,6 +582,16 @@ 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): + try: + conn.unwrap() + except ssl.SSLError, err: + if err.args[0] not in (ssl.SSL_ERROR_WANT_READ, + ssl.SSL_ERROR_WANT_WRITE): + raise + class FTP_TLS(FTP): '''A FTP subclass which adds TLS support to FTP as described in RFC-4217. @@ -684,7 +694,7 @@ callback(data) # shutdown ssl layer if isinstance(conn, ssl.SSLSocket): - conn.unwrap() + _ssl_unwrap(conn) finally: conn.close() return self.voidresp() @@ -707,7 +717,7 @@ callback(line) # shutdown ssl layer if isinstance(conn, ssl.SSLSocket): - conn.unwrap() + _ssl_unwrap(conn) finally: fp.close() conn.close() @@ -724,7 +734,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 +753,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,19 @@ 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 + # assert err.args[0] == ssl.SSL_ERROR_WANT_READ + except socket.error, err: + # the client dropped the connection + if err.args[0] not in (errno.EPIPE, 0): + raise finally: super(SSLConnection, self).close()