diff --git a/Lib/ftplib.py b/Lib/ftplib.py --- a/Lib/ftplib.py +++ b/Lib/ftplib.py @@ -343,19 +343,23 @@ class FTP: host, port = self.makepasv() conn = socket.create_connection((host, port), self.timeout, source_address=self.source_address) - if rest is not None: - self.sendcmd("REST %s" % rest) - resp = self.sendcmd(cmd) - # Some servers apparently send a 200 reply to - # a LIST or STOR command, before the 150 reply - # (and way before the 226 reply). This seems to - # be in violation of the protocol (which only allows - # 1xx or error messages for LIST), so we just discard - # this response. - if resp[0] == '2': - resp = self.getresp() - if resp[0] != '1': - raise error_reply(resp) + try: + if rest is not None: + self.sendcmd("REST %s" % rest) + resp = self.sendcmd(cmd) + # Some servers apparently send a 200 reply to + # a LIST or STOR command, before the 150 reply + # (and way before the 226 reply). This seems to + # be in violation of the protocol (which only allows + # 1xx or error messages for LIST), so we just discard + # this response. + if resp[0] == '2': + resp = self.getresp() + if resp[0] != '1': + raise error_reply(resp) + except: + conn.close() + raise else: sock = self.makeport() if rest is not None: diff --git a/Lib/test/test_urllib2net.py b/Lib/test/test_urllib2net.py --- a/Lib/test/test_urllib2net.py +++ b/Lib/test/test_urllib2net.py @@ -222,6 +222,7 @@ class OtherNetworkTests(unittest.TestCas handlers = [] cfh = urllib.request.CacheFTPHandler() + self.addCleanup(cfh.clear_cache) cfh.setTimeout(1) handlers.append(cfh) diff --git a/Lib/urllib/request.py b/Lib/urllib/request.py --- a/Lib/urllib/request.py +++ b/Lib/urllib/request.py @@ -1317,6 +1317,8 @@ def _safe_gethostbyname(host): return None class FTPHandler(BaseHandler): + _close_after_retr = True + def ftp_open(self, req): import ftplib import mimetypes @@ -1357,7 +1359,7 @@ class FTPHandler(BaseHandler): if attr.lower() == 'type' and \ value in ('a', 'A', 'i', 'I', 'd', 'D'): type = value.upper() - fp, retrlen = fw.retrfile(file, type) + fp, retrlen = fw.retrfile(file, type, close=self._close_after_retr) headers = "" mtype = mimetypes.guess_type(req.full_url)[0] if mtype: @@ -1375,6 +1377,8 @@ class FTPHandler(BaseHandler): return fw class CacheFTPHandler(FTPHandler): + _close_after_retr = False + # XXX would be nice to have pluggable cache strategies # XXX this stuff is definitely not thread safe def __init__(self): @@ -1421,6 +1425,12 @@ class CacheFTPHandler(FTPHandler): break self.soonest = min(list(self.timeout.values())) + def clear_cache(self): + for conn in self.cache.values(): + conn.close() + self.cache.clear() + + # Code move from the old urllib module MAXFTPCACHE = 10 # Trim the ftp cache beyond this size @@ -2162,7 +2172,7 @@ class ftpwrapper: for dir in self.dirs: self.ftp.cwd(dir) - def retrfile(self, file, type): + def retrfile(self, file, type, close=False): import ftplib self.endtransfer() if type in ('d', 'D'): cmd = 'TYPE A'; isdir = 1 @@ -2201,7 +2211,11 @@ class ftpwrapper: conn, retrlen = self.ftp.ntransfercmd(cmd) self.busy = 1 - ftpobj = addclosehook(conn.makefile('rb'), self.endtransfer) + if close: + close_func = self.close + else: + close_func = self.endtransfer + ftpobj = addclosehook(conn.makefile('rb'), close_func) conn.close() # Pass back both a suitably decorated object and a retrieval length return (ftpobj, retrlen)