Forzar solicitudes para usar IPv4 / IPv6

¿Cómo forzar a la biblioteca de requests a usar una versión específica del protocolo de Internet para una solicitud de obtención? ¿O puede lograrse mejor con otro método en Python? Podría pero no quiero usar curl

Ejemplo para aclarar propósito:

 import requests r = requests.get('https://my-dyn-dns-service.domain/?hostname=my.domain', auth = ('myUserName', 'my-password')) 

He encontrado una solución minimalista para forzar a urrlib3 a usar ipv4 o ipv6. Urrlib3 utiliza este método para crear una nueva conexión para Http y Https. Puedes especificar en ella cualquier AF_FAMILY que quieras usar.

 import socket import requests.packages.urllib3.util.connection as urllib3_cn def allowed_gai_family(): """ https://github.com/shazow/urllib3/blob/master/urllib3/util/connection.py """ family = socket.AF_INET if urllib3_cn.HAS_IPV6: family = socket.AF_INET6 # force ipv6 only if it is available return family urllib3_cn.allowed_gai_family = allowed_gai_family 

Esto es un truco, pero puede obtener el parche getaddrinfo para filtrar solo a direcciones IPv4:

 # Monkey patch to force IPv4, since FB seems to hang on IPv6 import socket old_getaddrinfo = socket.getaddrinfo def new_getaddrinfo(*args, **kwargs): responses = old_getaddrinfo(*args, **kwargs) return [response for response in responses if response[0] == socket.AF_INET] socket.getaddrinfo = new_getaddrinfo 

Esto no se ha probado totalmente y probablemente requerirá algunos ajustes, pero la combinación de las respuestas de Uso de Python “peticiones” con la conexión de socket existente y la forma de obligar a la biblioteca de python httplib a usar solo las solicitudes A , parece que debería ser capaz de crear un socket solo IPv6 y luego hacer que las solicitudes lo utilicen para su grupo de conexiones con algo como:

 try: from http.client import HTTPConnection except ImportError: from httplib import HTTPConnection class MyHTTPConnection(HTTPConnection): def connect(self): print("This actually called called") self.sock = socket.socket(socket.AF_INET6) self.sock.connect((self.host, self.port,0,0)) if self._tunnel_host: self._tunnel() requests.packages.urllib3.connectionpool.HTTPConnection = MyHTTPConnection 

Después de leer la respuesta anterior, tuve que modificar el código para forzar IPv4 en lugar de IPv6. Tenga en cuenta que usé socket.AF_INET en lugar de socket.AF_INET6, y self.sock.connect () tiene un argumento de tupla de 2 elementos.

También necesitaba anular la conexión HTTP S, que es muy diferente a HTTPConnection, ya que las requests envuelven el httplib.HTTPSConnection para verificar el certificado si el módulo ssl está disponible.

 import socket import ssl try: from http.client import HTTPConnection except ImportError: from httplib import HTTPConnection from requests.packages.urllib3.connection import VerifiedHTTPSConnection # HTTP class MyHTTPConnection(HTTPConnection): def connect(self): self.sock = socket.socket(socket.AF_INET) self.sock.connect((self.host, self.port)) if self._tunnel_host: self._tunnel() requests.packages.urllib3.connectionpool.HTTPConnection = MyHTTPConnection requests.packages.urllib3.connectionpool.HTTPConnectionPool.ConnectionCls = MyHTTPConnection # HTTPS class MyHTTPSConnection(VerifiedHTTPSConnection): def connect(self): self.sock = socket.socket(socket.AF_INET) self.sock.connect((self.host, self.port)) if self._tunnel_host: self._tunnel() self.sock = ssl.wrap_socket(self.sock, self.key_file, self.cert_file) requests.packages.urllib3.connectionpool.HTTPSConnection = MyHTTPSConnection requests.packages.urllib3.connectionpool.VerifiedHTTPSConnection = MyHTTPSConnection requests.packages.urllib3.connectionpool.HTTPSConnectionPool.ConnectionCls = MyHTTPSConnection 

Tomé un enfoque similar a https://stackoverflow.com/a/33046939/5059062 , pero en cambio reparé la parte en el socket que realiza las solicitudes de DNS por lo que solo hace IPv6 o IPv4, para cada solicitud, lo que significa que se puede usar en urllib tan eficazmente como en las requests .

Esto podría ser malo si su progtwig también utiliza tuberías de Unix y otras cosas similares, por lo que insto a la precaución con la reproducción de monos.

 import requests import socket from unittest.mock import patch import re orig_getaddrinfo = socket.getaddrinfo def getaddrinfoIPv6(host, port, family=0, type=0, proto=0, flags=0): return orig_getaddrinfo(host=host, port=port, family=socket.AF_INET6, type=type, proto=proto, flags=flags) def getaddrinfoIPv4(host, port, family=0, type=0, proto=0, flags=0): return orig_getaddrinfo(host=host, port=port, family=socket.AF_INET, type=type, proto=proto, flags=flags) with patch('socket.getaddrinfo', side_effect=getaddrinfoIPv6): r = requests.get('http://ip6.me') print('ipv6: '+re.search(r'\+3>(.*?)(.*?) 

y sin requests :

 import urllib.request import socket from unittest.mock import patch import re orig_getaddrinfo = socket.getaddrinfo def getaddrinfoIPv6(host, port, family=0, type=0, proto=0, flags=0): return orig_getaddrinfo(host=host, port=port, family=socket.AF_INET6, type=type, proto=proto, flags=flags) def getaddrinfoIPv4(host, port, family=0, type=0, proto=0, flags=0): return orig_getaddrinfo(host=host, port=port, family=socket.AF_INET, type=type, proto=proto, flags=flags) with patch('socket.getaddrinfo', side_effect=getaddrinfoIPv6): r = urllib.request.urlopen('http://ip6.me') print('ipv6: '+re.search(r'\+3>(.*?)(.*?) 

Probado en 3.5.2