2014-03-24 70 views
7

在我的項目中,我使用python requests library處理所有HTTP請求。Python'請求'庫 - 定義特定的DNS?

現在,我需要使用特定的DNS查詢http服務器 - 有兩種環境,每種都使用自己的DNS,並且獨立進行更改。

所以,當代碼運行時,它應該使用特定於環境的DNS,而不是在我的Internet連接中指定的DNS。

有沒有人嘗試過使用python-requests?我只找到了解決的urllib2:
https://stackoverflow.com/questions/4623090/python-set-custom-dns-server-for-urllib-requests

+0

答案基本相同; 'urllib2'和'requests'都使用相同的'httplib.HTTPConnection'類。 –

+0

你是什麼意思?要麼你正在執行一個DNS查詢,要麼你正在執行一個HTTP請求......你不要一次執行這兩個操作。你的目標是什麼? –

+0

我正在執行一個使用域名的http請求 - 因爲當我使用DNS時域名不會被解析(我提供的域名在其中一個我提到的環境中有DNS域名文件_only_),而在其他地方 - 因爲它們是測試環境)。 – Taku

回答

14

requests使用urllib3,最終採用httplib.HTTPConnection一樣,所以從https://stackoverflow.com/questions/4623090/python-set-custom-dns-server-for-urllib-requests(現已刪除,它只是掛Tell urllib2 to use custom DNS)的技術仍然適用,在一定程度。

urllib3.connection模塊小類httplib.HTTPConnection在相同的名稱,已取代.connect()方法與一個調用self._new_conn。反過來,這個代表urllib3.util.connection.create_connection()。這也許是最簡單的修補功能:

from urllib3.util import connection 


_orig_create_connection = connection.create_connection 


def patched_create_connection(address, *args, **kwargs): 
    """Wrap urllib3's create_connection to resolve the name elsewhere""" 
    # resolve hostname to an ip address; use your own 
    # resolver here, as otherwise the system resolver will be used. 
    host, port = address 
    hostname = your_dns_resolver(host) 

    return _orig_create_connection((hostname, port), *args, **kwargs) 


connection.create_connection = patched_create_connection 

而且你提供你自己的代碼地址的host部分解析爲IP地址,而不是依靠connection.create_connection()調用(包socket.create_connection())爲您解析主機名。

與所有monkeypatching一樣,請注意代碼在以後的版本中沒有顯着變化;這裏的補丁是針對urllib3版本1.21.1創建的。但應該可以用於1.9版本的版本。


注意,這個答案重寫與新urllib3版本,其中增加了一個更方便的修補位置的工作。請參閱適用於版本< 1.9的舊方法的編輯歷史記錄,作爲出售urllib3版本的補丁,而不是獨立安裝。

+0

是的!完美的作品。 對於分解器中,我使用: 'DEF myResolver(主機,dnssrv): \t R = dns.resolver.Resolver() \t r.nameservers = dnssrv \t答案= r.query(主機,「A 「) \t爲RDATA的答案: \t \t回STR(RDATA)' 現在每個請求我能夠確定哪些DNS應解析主機名,動態。 非常非常感謝你。 – Taku

+1

請注意,您可能需要根據您的本地版本的請求庫來適應上述情況。 Martijn Pieters的代碼匹配版本2.2或其附件。在2.4中,HTTPConnection對象具有不同的屬性,並且與代碼稍有不同。由於你在這裏使用lib的內部細節,所以修復很可能是特定於版本的。 –

+0

這項工作是否能及時通過SSL檢查? IE瀏覽器。我有一個服務器設置在10.0.0.1具有example.com證書...如果我使用此方法將「example.com」更改爲「10.0.0.1」,我會從請求中獲得SSL錯誤嗎? – laughingbovine

10

你應該看看TransportAdapters,包括源代碼。他們的文檔不是很好,但他們可以低級訪問RFC 2818RFC 6125中描述的許多功能。尤其是,這些文件鼓勵(需要)客戶端代碼支持特定於應用程序的DNS,以檢查證書CommonName和SubjectAltName。您在這些調用中需要的關鍵字參數是「assert_hostname」。以下是如何與請求庫設置:

from requests import Session, HTTPError 
from requests.adapters import HTTPAdapter, DEFAULT_POOLSIZE, DEFAULT_RETRIES, DEFAULT_POOLBLOCK 


class DNSResolverHTTPSAdapter(HTTPAdapter): 
    def __init__(self, common_name, host, pool_connections=DEFAULT_POOLSIZE, pool_maxsize=DEFAULT_POOLSIZE, 
     max_retries=DEFAULT_RETRIES, pool_block=DEFAULT_POOLBLOCK): 
     self.__common_name = common_name 
     self.__host = host 
     super(DNSResolverHTTPSAdapter, self).__init__(pool_connections=pool_connections, pool_maxsize=pool_maxsize, 
      max_retries=max_retries, pool_block=pool_block) 

    def get_connection(self, url, proxies=None): 
     redirected_url = url.replace(self.__common_name, self.__host) 
     return super(DNSResolverHTTPSAdapter, self).get_connection(redirected_url, proxies=proxies) 

    def init_poolmanager(self, connections, maxsize, block=DEFAULT_POOLBLOCK, **pool_kwargs): 
     pool_kwargs['assert_hostname'] = self.__common_name 
     super(DNSResolverHTTPSAdapter, self).init_poolmanager(connections, maxsize, block=block, **pool_kwargs) 

common_name = 'SuperSecretSarahServer' 
host = '192.168.33.51' 
port = 666 
base_url = 'https://{}:{}/api/'.format(common_name, port) 
my_session = Session() 
my_session.mount(self.base_url.lower(), DNSResolverHTTPSAdapter(common_name, host)) 
user_name = 'sarah' 
url = '{}users/{}'.format(self.base_url, user_name) 
default_response_kwargs = { 
    'auth': (NAME, PASSWORD), 
    'headers': {'Content-Type': 'application/json'}, 
    'verify': SSL_OPTIONS['ca_certs'], 
    'cert': (SSL_OPTIONS['certfile'], SSL_OPTIONS['keyfile']) 
} 
response = my_session.get(url, **default_response_kwargs) 

我用common_name爲預計證書,以及如何您的代碼將引用所需的機器上的名字。我使用host作爲外部世界認可的名稱 - FQDN,IP,DNS條目,......當然,SSL_OPTIONS字典(在我的示例中)必須在您的機器上列出適當的證書/密鑰文件名。 (另外,NAME和PASSWORD應解析爲更正字符串。)

+1

我認爲這應該是正確的答案,因爲這是使用請求和urllib3完成此操作的正確方法。 –