¿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>(.*?)',r.content.decode('utf-8')).group(1)) with patch('socket.getaddrinfo', side_effect=getaddrinfoIPv4): r = requests.get('http://ip6.me') print('ipv4: '+re.search(r'\+3>(.*?)',r.content.decode('utf-8')).group(1))
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>(.*?)',r.read().decode('utf-8')).group(1)) with patch('socket.getaddrinfo', side_effect=getaddrinfoIPv4): r = urllib.request.urlopen('http://ip6.me') print('ipv4: '+re.search(r'\+3>(.*?)',r.read().decode('utf-8')).group(1))
Probado en 3.5.2