Index: Lib/ssl.py =================================================================== --- Lib/ssl.py (révision 78840) +++ Lib/ssl.py (copie de travail) @@ -77,6 +77,7 @@ from socket import socket, _fileobject, error as socket_error from socket import getnameinfo as _getnameinfo import base64 # for DER-to-PEM translation +from errno import EPIPE class SSLSocket(socket): @@ -268,8 +269,41 @@ return 0 def unwrap(self): + """ + Shutdown the SSL connection. It may fail with an + SSLError(SSL_ERROR_WANT_READ, ...) for a non blocking socket: the + caller will have to call its read() method. + """ if self._sslobj: - s = self._sslobj.shutdown() + s = None + while 1: + try: + s = self._sslobj.shutdown() + except SSLError as err: + if err.args[0] == SSL_ERROR_WANT_READ: + try: + self.read() + except SSLError as read_err: + if read_err.args[0] == SSL_ERROR_ZERO_RETURN: + # connection closed: done + s = None + break + else: + # non blocking socket + raise err + else: + continue + else: + raise + except socket_error as err: + if err.errno == EPIPE: + # connection closed: done + s = None + break + else: + raise + else: + break self._sslobj = None return s else: Index: Lib/test/test_ftplib.py =================================================================== --- Lib/test/test_ftplib.py (révision 78840) +++ Lib/test/test_ftplib.py (copie de travail) @@ -309,13 +309,18 @@ def handle_error(self): raise - def close(self): - try: - if isinstance(self.socket, ssl.SSLSocket): - if self.socket._sslobj is not None: + def handle_close(self): + if isinstance(self.socket, ssl.SSLSocket): + if self.socket._sslobj is not None: + try: self.socket.unwrap() - finally: - super(SSLConnection, self).close() + except ssl.SSLError as err: + if err.args[0] == ssl.SSL_ERROR_WANT_READ: + # call handle_read() + return + else: + raise + super(SSLConnection, self).handle_close() class DummyTLS_DTPHandler(SSLConnection, DummyDTPHandler):