(self, request: Request)
| 93 | self.passive_mode = crawler.settings["FTP_PASSIVE_MODE"] |
| 94 | |
| 95 | async def download_request(self, request: Request) -> Response: |
| 96 | from twisted.internet import reactor |
| 97 | from twisted.protocols.ftp import CommandFailed, FTPClient |
| 98 | |
| 99 | parsed_url = urlparse_cached(request) |
| 100 | user = request.meta.get("ftp_user", self.default_user) |
| 101 | password = request.meta.get("ftp_password", self.default_password) |
| 102 | passive_mode = ( |
| 103 | 1 if bool(request.meta.get("ftp_passive", self.passive_mode)) else 0 |
| 104 | ) |
| 105 | creator = ClientCreator( |
| 106 | reactor, FTPClient, user, password, passive=passive_mode |
| 107 | ) |
| 108 | client: FTPClient = await maybe_deferred_to_future( |
| 109 | creator.connectTCP(parsed_url.hostname, parsed_url.port or 21) |
| 110 | ) |
| 111 | filepath = unquote(parsed_url.path) |
| 112 | protocol = ReceivedDataProtocol(request.meta.get("ftp_local_filename")) |
| 113 | try: |
| 114 | await maybe_deferred_to_future(client.retrieveFile(filepath, protocol)) |
| 115 | except CommandFailed as e: |
| 116 | message = str(e) |
| 117 | if m := _CODE_RE.search(message): |
| 118 | ftpcode = m.group() |
| 119 | httpcode = self.CODE_MAPPING.get(ftpcode, self.CODE_MAPPING["default"]) |
| 120 | return Response(url=request.url, status=httpcode, body=message.encode()) |
| 121 | raise |
| 122 | protocol.close() |
| 123 | headers = {"local filename": protocol.filename or b"", "size": protocol.size} |
| 124 | body = protocol.filename or protocol.body.read() |
| 125 | respcls = responsetypes.from_args(url=request.url, body=body) |
| 126 | # hints for Headers-related types may need to be fixed to not use AnyStr |
| 127 | return respcls(url=request.url, status=200, body=body, headers=headers) # type: ignore[arg-type] |
nothing calls this directly
no test coverage detected