2009-08-18 32 views
6

Tôi đang phát triển một ứng dụng khách FTP trong Python ftplib. Làm cách nào để thêm hỗ trợ proxy vào nó (hầu hết các ứng dụng FTP mà tôi thấy có vẻ như có)? Tôi đặc biệt nghĩ về proxy SOCKS, nhưng cũng có các loại khác ... FTP, HTTP (thậm chí có thể sử dụng proxy HTTP với chương trình FTP không?)Proxy trong ứng dụng Python của Python

Bất kỳ ý tưởng nào để làm điều đó?

Trả lời

1

Mô-đun chuẩn ftplib không hỗ trợ proxy. Có vẻ như giải pháp duy nhất là viết phiên bản tùy chỉnh của riêng bạn của ftplib.

6

Theo nguồn this.

Phụ thuộc vào proxy, nhưng một phương pháp phổ biến là ftp vào proxy, sau đó sử dụng tên người dùng và mật khẩu cho máy chủ đích.

Ví dụ: cho ftp.example.com:

Server address: proxyserver (or open proxyserver from with ftp) 
User:   [email protected] 
Password:  password 

Trong mã Python:

from ftplib import FTP 
site = FTP('my_proxy') 
site.set_debuglevel(1) 
msg = site.login('[email protected]', 'password') 
site.cwd('/pub') 
+0

vào liên kết trong câu trả lời trên là 404. Có thể có nghĩa là cái này: http : //mail.python.org/pipermail/python-list/2004-October/863602.html – AndrewR

+3

Phần "ẩn danh tại ftp.download.com" là tiểu thuyết thuần túy. Không có điều gì giống như vậy từng được đề cập trong bất kỳ RFC hoặc nào được triển khai/hỗ trợ bởi bất kỳ máy chủ nào, theo như tôi biết. Tương đối, giao thức FTP không hỗ trợ proxy. AFAIK, cách duy nhất để proxy FTP là bằng cách sử dụng một SOCKS trong trường hợp máy khách được cho là kết nối với SOCKS và lệnh thứ hai cần được hướng dẫn về máy chủ FTP thực. –

+1

Câu trả lời này giải quyết cho tôi một cơn đau đầu lớn. Cảm ơn bạn!!! – dgg32

4

Bạn có thể sử dụng ProxyHandler trong urllib2.

ph = urllib2.ProxyHandler({ 'ftp' : proxy_server_url }) 
server= urllib2.build_opener(ph) 
+0

Lỗi đánh máy "urlli2" trong ví dụ không thể chỉnh sửa được vì "Chỉnh sửa phải có ít nhất 6 ký tự". – HongboZhu

3

tôi đã cùng một vấn đề và cần thiết để sử dụng các mô-đun ftplib (không phải viết lại tất cả các kịch bản của tôi với urllib2).

Tôi đã quản lý để viết tập lệnh cài đặt minh bạch Đường hầm HTTP trên lớp ổ cắm (được sử dụng bởi ftplib).

Bây giờ, tôi có thể thực hiện FTP qua HTTP một cách minh bạch!

Bạn có thể lấy nó ở đó: http://code.activestate.com/recipes/577643-transparent-http-tunnel-for-python-sockets-to-be-u/

0

Patching các thư viện socket BUILTIN chắc chắn sẽ không phải là một lựa chọn cho tất cả mọi người, nhưng giải pháp của tôi là để vá socket.create_connection() sử dụng một proxy HTTP khi hostname phù hợp với một danh sách trắng:

from base64 import b64encode 
from functools import wraps 
import socket 

_real_create_connection = socket.create_connection 
_proxied_hostnames = {} # hostname: (proxy_host, proxy_port, proxy_auth) 


def register_proxy (host, proxy_host, proxy_port, proxy_username=None, proxy_password=None): 
    proxy_auth = None 
    if proxy_username is not None or proxy_password is not None: 
     proxy_auth = b64encode('{}:{}'.format(proxy_username or '', proxy_password or '')) 
    _proxied_hostnames[host] = (proxy_host, proxy_port, proxy_auth) 


@wraps(_real_create_connection) 
def create_connection (address, *args, **kwds): 
    host, port = address 
    if host not in _proxied_hostnames: 
     return _real_create_connection(address, *args, **kwds) 

    proxy_host, proxy_port, proxy_auth = _proxied_hostnames[host] 
    conn = _real_create_connection((proxy_host, proxy_port), *args, **kwds) 
    try: 
     conn.send('CONNECT {host}:{port} HTTP/1.1\r\nHost: {host}:{port}\r\n{auth_header}\r\n'.format(
      host=host, port=port, 
      auth_header=('Proxy-Authorization: basic {}\r\n'.format(proxy_auth) if proxy_auth else '') 
     )) 
     response = '' 
     while not response.endswith('\r\n\r\n'): 
      response += conn.recv(4096) 
     if response.split()[1] != '200': 
      raise socket.error('CONNECT failed: {}'.format(response.strip())) 
    except socket.error: 
     conn.close() 
     raise 

    return conn 


socket.create_connection = create_connection 

tôi cũng đã phải tạo ra một lớp con của ftplib.FTP mà bỏ qua host trả về bởi PASVEPSV lệnh FTP. sử dụng ví dụ:

from ftplib import FTP 
import paramiko # For SFTP 
from proxied_socket import register_proxy 

class FTPIgnoreHost (FTP): 
    def makepasv (self): 
     # Ignore the host returned by PASV or EPSV commands (only use the port). 
     return self.host, FTP.makepasv(self)[1] 

register_proxy('ftp.example.com', 'proxy.example.com', 3128, 'proxy_username', 'proxy_password') 

ftp_connection = FTP('ftp.example.com', 'ftp_username', 'ftp_password') 

ssh = paramiko.SSHClient() 
ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy()) # If you don't care about security. 
ssh.connect('ftp.example.com', username='sftp_username', password='sftp_password') 
sftp_connection = ssh.open_sftp() 
0

Dưới đây là cách giải quyết bằng requests, thử nghiệm với một proxy mực mà KHÔNG hỗ trợ CONNECT đường hầm:

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:[email protected]$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 
Các vấn đề liên quan