1919from .compat import urlparse , basestring
2020from .utils import (DEFAULT_CA_BUNDLE_PATH , get_encoding_from_headers ,
2121 prepend_scheme_if_needed , get_auth_from_url , urldefragauth ,
22- select_proxy )
22+ select_proxy , to_native_string )
2323from .structures import CaseInsensitiveDict
2424from .packages .urllib3 .exceptions import ClosedPoolError
2525from .packages .urllib3 .exceptions import ConnectTimeoutError
3333from .packages .urllib3 .exceptions import ResponseError
3434from .cookies import extract_cookies_to_jar
3535from .exceptions import (ConnectionError , ConnectTimeout , ReadTimeout , SSLError ,
36- ProxyError , RetryError )
36+ ProxyError , RetryError , InvalidSchema )
3737from .auth import _basic_auth_str
3838
39+ try :
40+ from .packages .urllib3 .contrib .socks import SOCKSProxyManager
41+ except ImportError :
42+ def SOCKSProxyManager (* args , ** kwargs ):
43+ raise InvalidSchema ("Missing dependencies for SOCKS support." )
44+
3945DEFAULT_POOLBLOCK = False
4046DEFAULT_POOLSIZE = 10
4147DEFAULT_RETRIES = 0
@@ -48,10 +54,24 @@ class BaseAdapter(object):
4854 def __init__ (self ):
4955 super (BaseAdapter , self ).__init__ ()
5056
51- def send (self ):
57+ def send (self , request , stream = False , timeout = None , verify = True ,
58+ cert = None , proxies = None ):
59+ """Sends PreparedRequest object. Returns Response object.
60+
61+ :param request: The :class:`PreparedRequest <PreparedRequest>` being sent.
62+ :param stream: (optional) Whether to stream the request content.
63+ :param timeout: (optional) How long to wait for the server to send
64+ data before giving up, as a float, or a :ref:`(connect timeout,
65+ read timeout) <timeouts>` tuple.
66+ :type timeout: float or tuple
67+ :param verify: (optional) Whether to verify SSL certificates.
68+ :param cert: (optional) Any user-provided SSL certificate to be trusted.
69+ :param proxies: (optional) The proxies dictionary to apply to the request.
70+ """
5271 raise NotImplementedError
5372
5473 def close (self ):
74+ """Cleans up adapter specific items."""
5575 raise NotImplementedError
5676
5777
@@ -65,7 +85,7 @@ class HTTPAdapter(BaseAdapter):
6585
6686 :param pool_connections: The number of urllib3 connection pools to cache.
6787 :param pool_maxsize: The maximum number of connections to save in the pool.
68- :param int max_retries: The maximum number of retries each connection
88+ :param max_retries: The maximum number of retries each connection
6989 should attempt. Note, this applies only to failed DNS lookups, socket
7090 connections and connection timeouts, never to requests where data has
7191 made it to the server. By default, Requests does not retry failed
@@ -148,18 +168,32 @@ def proxy_manager_for(self, proxy, **proxy_kwargs):
148168 :param proxy: The proxy to return a urllib3 ProxyManager for.
149169 :param proxy_kwargs: Extra keyword arguments used to configure the Proxy Manager.
150170 :returns: ProxyManager
171+ :rtype: requests.packages.urllib3.ProxyManager
151172 """
152- if not proxy in self .proxy_manager :
173+ if proxy in self .proxy_manager :
174+ manager = self .proxy_manager [proxy ]
175+ elif proxy .lower ().startswith ('socks' ):
176+ username , password = get_auth_from_url (proxy )
177+ manager = self .proxy_manager [proxy ] = SOCKSProxyManager (
178+ proxy ,
179+ username = username ,
180+ password = password ,
181+ num_pools = self ._pool_connections ,
182+ maxsize = self ._pool_maxsize ,
183+ block = self ._pool_block ,
184+ ** proxy_kwargs
185+ )
186+ else :
153187 proxy_headers = self .proxy_headers (proxy )
154- self .proxy_manager [proxy ] = proxy_from_url (
188+ manager = self .proxy_manager [proxy ] = proxy_from_url (
155189 proxy ,
156190 proxy_headers = proxy_headers ,
157191 num_pools = self ._pool_connections ,
158192 maxsize = self ._pool_maxsize ,
159193 block = self ._pool_block ,
160194 ** proxy_kwargs )
161195
162- return self . proxy_manager [ proxy ]
196+ return manager
163197
164198 def cert_verify (self , conn , url , verify , cert ):
165199 """Verify a SSL certificate. This method should not be called from user
@@ -211,6 +245,7 @@ def build_response(self, req, resp):
211245
212246 :param req: The :class:`PreparedRequest <PreparedRequest>` used to generate the response.
213247 :param resp: The urllib3 response object.
248+ :rtype: requests.Response
214249 """
215250 response = Response ()
216251
@@ -246,6 +281,7 @@ def get_connection(self, url, proxies=None):
246281
247282 :param url: The URL to connect to.
248283 :param proxies: (optional) A Requests-style dictionary of proxies used on this request.
284+ :rtype: requests.packages.urllib3.ConnectionPool
249285 """
250286 proxy = select_proxy (url , proxies )
251287
@@ -264,10 +300,12 @@ def get_connection(self, url, proxies=None):
264300 def close (self ):
265301 """Disposes of any internal state.
266302
267- Currently, this just closes the PoolManager, which closes pooled
268- connections.
303+ Currently, this closes the PoolManager and any active ProxyManager,
304+ which closes any pooled connections.
269305 """
270306 self .poolmanager .clear ()
307+ for proxy in self .proxy_manager .values ():
308+ proxy .clear ()
271309
272310 def request_url (self , request , proxies ):
273311 """Obtain the url to use when making the final request.
@@ -281,13 +319,20 @@ def request_url(self, request, proxies):
281319
282320 :param request: The :class:`PreparedRequest <PreparedRequest>` being sent.
283321 :param proxies: A dictionary of schemes or schemes and hosts to proxy URLs.
322+ :rtype: str
284323 """
285324 proxy = select_proxy (request .url , proxies )
286325 scheme = urlparse (request .url ).scheme
287- if proxy and scheme != 'https' :
326+
327+ is_proxied_http_request = (proxy and scheme != 'https' )
328+ using_socks_proxy = False
329+ if proxy :
330+ proxy_scheme = urlparse (proxy ).scheme .lower ()
331+ using_socks_proxy = proxy_scheme .startswith ('socks' )
332+
333+ url = request .path_url
334+ if is_proxied_http_request and not using_socks_proxy :
288335 url = urldefragauth (request .url )
289- else :
290- url = request .path_url
291336
292337 return url
293338
@@ -316,11 +361,12 @@ def proxy_headers(self, proxy):
316361 :class:`HTTPAdapter <requests.adapters.HTTPAdapter>`.
317362
318363 :param proxies: The url of the proxy being used for this request.
364+ :rtype: dict
319365 """
320366 headers = {}
321367 username , password = get_auth_from_url (proxy )
322368
323- if username and password :
369+ if username :
324370 headers ['Proxy-Authorization' ] = _basic_auth_str (username ,
325371 password )
326372
@@ -338,6 +384,7 @@ def send(self, request, stream=False, timeout=None, verify=True, cert=None, prox
338384 :param verify: (optional) Whether to verify SSL certificates.
339385 :param cert: (optional) Any user-provided SSL certificate to be trusted.
340386 :param proxies: (optional) The proxies dictionary to apply to the request.
387+ :rtype: requests.Response
341388 """
342389
343390 conn = self .get_connection (request .url , proxies )
@@ -434,6 +481,9 @@ def send(self, request, stream=False, timeout=None, verify=True, cert=None, prox
434481 if isinstance (e .reason , ResponseError ):
435482 raise RetryError (e , request = request )
436483
484+ if isinstance (e .reason , _ProxyError ):
485+ raise ProxyError (e , request = request )
486+
437487 raise ConnectionError (e , request = request )
438488
439489 except ClosedPoolError as e :
0 commit comments