From dfaabca69678b84a32d66816732ef16f65795ebb Mon Sep 17 00:00:00 2001 From: factnn <166481866+factnn@users.noreply.github.com> Date: Tue, 16 Jun 2026 16:08:26 +0800 Subject: [PATCH] Fix FTPDownloadHandler leaking connections and protocol buffers Close FTPClient via quit() in a finally block and close ReceivedDataProtocol in the CommandFailed error path. Fixes #7602 --- scrapy/core/downloader/handlers/ftp.py | 21 ++++++++++++--------- 1 file changed, 12 insertions(+), 9 deletions(-) diff --git a/scrapy/core/downloader/handlers/ftp.py b/scrapy/core/downloader/handlers/ftp.py index 6258067c1..14ff9112d 100644 --- a/scrapy/core/downloader/handlers/ftp.py +++ b/scrapy/core/downloader/handlers/ftp.py @@ -111,15 +111,18 @@ class FTPDownloadHandler(BaseDownloadHandler): filepath = unquote(parsed_url.path) protocol = ReceivedDataProtocol(request.meta.get("ftp_local_filename")) try: - await maybe_deferred_to_future(client.retrieveFile(filepath, protocol)) - except CommandFailed as e: - message = str(e) - if m := _CODE_RE.search(message): - ftpcode = m.group() - httpcode = self.CODE_MAPPING.get(ftpcode, self.CODE_MAPPING["default"]) - return Response(url=request.url, status=httpcode, body=message.encode()) - raise - protocol.close() + try: + await maybe_deferred_to_future(client.retrieveFile(filepath, protocol)) + except CommandFailed as e: + message = str(e) + if m := _CODE_RE.search(message): + ftpcode = m.group() + httpcode = self.CODE_MAPPING.get(ftpcode, self.CODE_MAPPING["default"]) + return Response(url=request.url, status=httpcode, body=message.encode()) + raise + protocol.close() + finally: + await maybe_deferred_to_future(client.quit()) headers = {"local filename": protocol.filename or b"", "size": protocol.size} body = protocol.filename or protocol.body.read() respcls = responsetypes.from_args(url=request.url, body=body)