Proxies in Python FTP application

前端 未结 6 634
青春惊慌失措
青春惊慌失措 2020-12-15 11:42

I\'m developing an FTP client in Python ftplib. How do I add proxies support to it (most FTP apps I have seen seem to have it)? I\'m especially thinking about SOCKS proxies,

6条回答
  •  一个人的身影
    2020-12-15 12:18

    Here is workaround using requests, tested with a squid proxy that does NOT support CONNECT tunneling:

    def ftp_fetch_file_through_http_proxy(host, user, password, remote_filepath, http_proxy, output_filepath):
        """
        This function let us to make a FTP RETR query through a HTTP proxy that does NOT support CONNECT tunneling.
        It is equivalent to: curl -x $HTTP_PROXY --user $USER:$PASSWORD ftp://$FTP_HOST/path/to/file
        It returns the 'Last-Modified' HTTP header value from the response.
    
        More precisely, this function sends the following HTTP request to $HTTP_PROXY:
            GET ftp://$USER:$PASSWORD@$FTP_HOST/path/to/file HTTP/1.1
        Note that in doing so, the host in the request line does NOT match the host we send this packet to.
    
        Python `requests` lib does not let us easily "cheat" like this.
        In order to achieve what we want, we need:
        - to mock urllib3.poolmanager.parse_url so that it returns a (host,port) pair indicating to send the request to the proxy
        - to register a connection adapter to the 'ftp://' prefix. This is basically a HTTP adapter but it uses the FULL url of
        the resource to build the request line, instead of only its relative path.
        """
        url = 'ftp://{}:{}@{}/{}'.format(user, password, host, remote_filepath)
        proxy_host, proxy_port = http_proxy.split(':')
    
        def parse_url_mock(url):
            return requests.packages.urllib3.util.url.parse_url(url)._replace(host=proxy_host, port=proxy_port, scheme='http')
    
        with open(output_filepath, 'w+b') as output_file, patch('requests.packages.urllib3.poolmanager.parse_url', new=parse_url_mock):
            session = requests.session()
            session.mount('ftp://', FTPWrappedInFTPAdapter())
            response = session.get(url)
            response.raise_for_status()
            output_file.write(response.content)
            return response.headers['last-modified']
    
    
    class FTPWrappedInFTPAdapter(requests.adapters.HTTPAdapter):
        def request_url(self, request, _):
            return request.url
    

提交回复
热议问题