1 # -*- coding: utf-8 -*-
7 This module contains the transport adapters that Requests uses to define
8 and maintain connections.
14 from .models import Response
15 from .packages.urllib3.poolmanager import PoolManager, proxy_from_url
16 from .packages.urllib3.response import HTTPResponse
17 from .packages.urllib3.util import Timeout as TimeoutSauce
18 from .packages.urllib3.util.retry import Retry
19 from .compat import urlparse, basestring
20 from .utils import (DEFAULT_CA_BUNDLE_PATH, get_encoding_from_headers,
21 prepend_scheme_if_needed, get_auth_from_url, urldefragauth,
22 select_proxy, to_native_string)
23 from .structures import CaseInsensitiveDict
24 from .packages.urllib3.exceptions import ClosedPoolError
25 from .packages.urllib3.exceptions import ConnectTimeoutError
26 from .packages.urllib3.exceptions import HTTPError as _HTTPError
27 from .packages.urllib3.exceptions import MaxRetryError
28 from .packages.urllib3.exceptions import NewConnectionError
29 from .packages.urllib3.exceptions import ProxyError as _ProxyError
30 from .packages.urllib3.exceptions import ProtocolError
31 from .packages.urllib3.exceptions import ReadTimeoutError
32 from .packages.urllib3.exceptions import SSLError as _SSLError
33 from .packages.urllib3.exceptions import ResponseError
34 from .cookies import extract_cookies_to_jar
35 from .exceptions import (ConnectionError, ConnectTimeout, ReadTimeout, SSLError,
36 ProxyError, RetryError, InvalidSchema)
37 from .auth import _basic_auth_str
40 from .packages.urllib3.contrib.socks import SOCKSProxyManager
42 def SOCKSProxyManager(*args, **kwargs):
43 raise InvalidSchema("Missing dependencies for SOCKS support.")
45 DEFAULT_POOLBLOCK = False
48 DEFAULT_POOL_TIMEOUT = None
51 class BaseAdapter(object):
52 """The Base Transport Adapter"""
55 super(BaseAdapter, self).__init__()
58 raise NotImplementedError
61 raise NotImplementedError
64 class HTTPAdapter(BaseAdapter):
65 """The built-in HTTP Adapter for urllib3.
67 Provides a general-case interface for Requests sessions to contact HTTP and
68 HTTPS urls by implementing the Transport Adapter interface. This class will
69 usually be created by the :class:`Session <Session>` class under the
72 :param pool_connections: The number of urllib3 connection pools to cache.
73 :param pool_maxsize: The maximum number of connections to save in the pool.
74 :param max_retries: The maximum number of retries each connection
75 should attempt. Note, this applies only to failed DNS lookups, socket
76 connections and connection timeouts, never to requests where data has
77 made it to the server. By default, Requests does not retry failed
78 connections. If you need granular control over the conditions under
79 which we retry a request, import urllib3's ``Retry`` class and pass
81 :param pool_block: Whether the connection pool should block for connections.
86 >>> s = requests.Session()
87 >>> a = requests.adapters.HTTPAdapter(max_retries=3)
88 >>> s.mount('http://', a)
90 __attrs__ = ['max_retries', 'config', '_pool_connections', '_pool_maxsize',
93 def __init__(self, pool_connections=DEFAULT_POOLSIZE,
94 pool_maxsize=DEFAULT_POOLSIZE, max_retries=DEFAULT_RETRIES,
95 pool_block=DEFAULT_POOLBLOCK):
96 if max_retries == DEFAULT_RETRIES:
97 self.max_retries = Retry(0, read=False)
99 self.max_retries = Retry.from_int(max_retries)
101 self.proxy_manager = {}
103 super(HTTPAdapter, self).__init__()
105 self._pool_connections = pool_connections
106 self._pool_maxsize = pool_maxsize
107 self._pool_block = pool_block
109 self.init_poolmanager(pool_connections, pool_maxsize, block=pool_block)
111 def __getstate__(self):
112 return dict((attr, getattr(self, attr, None)) for attr in
115 def __setstate__(self, state):
116 # Can't handle by adding 'proxy_manager' to self.__attrs__ because
117 # self.poolmanager uses a lambda function, which isn't pickleable.
118 self.proxy_manager = {}
121 for attr, value in state.items():
122 setattr(self, attr, value)
124 self.init_poolmanager(self._pool_connections, self._pool_maxsize,
125 block=self._pool_block)
127 def init_poolmanager(self, connections, maxsize, block=DEFAULT_POOLBLOCK, **pool_kwargs):
128 """Initializes a urllib3 PoolManager.
130 This method should not be called from user code, and is only
131 exposed for use when subclassing the
132 :class:`HTTPAdapter <requests.adapters.HTTPAdapter>`.
134 :param connections: The number of urllib3 connection pools to cache.
135 :param maxsize: The maximum number of connections to save in the pool.
136 :param block: Block when no free connections are available.
137 :param pool_kwargs: Extra keyword arguments used to initialize the Pool Manager.
139 # save these values for pickling
140 self._pool_connections = connections
141 self._pool_maxsize = maxsize
142 self._pool_block = block
144 self.poolmanager = PoolManager(num_pools=connections, maxsize=maxsize,
145 block=block, strict=True, **pool_kwargs)
147 def proxy_manager_for(self, proxy, **proxy_kwargs):
148 """Return urllib3 ProxyManager for the given proxy.
150 This method should not be called from user code, and is only
151 exposed for use when subclassing the
152 :class:`HTTPAdapter <requests.adapters.HTTPAdapter>`.
154 :param proxy: The proxy to return a urllib3 ProxyManager for.
155 :param proxy_kwargs: Extra keyword arguments used to configure the Proxy Manager.
156 :returns: ProxyManager
158 if proxy in self.proxy_manager:
159 manager = self.proxy_manager[proxy]
160 elif proxy.lower().startswith('socks'):
161 username, password = get_auth_from_url(proxy)
162 manager = self.proxy_manager[proxy] = SOCKSProxyManager(
166 num_pools=self._pool_connections,
167 maxsize=self._pool_maxsize,
168 block=self._pool_block,
172 proxy_headers = self.proxy_headers(proxy)
173 manager = self.proxy_manager[proxy] = proxy_from_url(
175 proxy_headers=proxy_headers,
176 num_pools=self._pool_connections,
177 maxsize=self._pool_maxsize,
178 block=self._pool_block,
183 def cert_verify(self, conn, url, verify, cert):
184 """Verify a SSL certificate. This method should not be called from user
185 code, and is only exposed for use when subclassing the
186 :class:`HTTPAdapter <requests.adapters.HTTPAdapter>`.
188 :param conn: The urllib3 connection object associated with the cert.
189 :param url: The requested URL.
190 :param verify: Whether we should actually verify the certificate.
191 :param cert: The SSL certificate to verify.
193 if url.lower().startswith('https') and verify:
197 # Allow self-specified cert location.
198 if verify is not True:
202 cert_loc = DEFAULT_CA_BUNDLE_PATH
205 raise Exception("Could not find a suitable SSL CA certificate bundle.")
207 conn.cert_reqs = 'CERT_REQUIRED'
209 if not os.path.isdir(cert_loc):
210 conn.ca_certs = cert_loc
212 conn.ca_cert_dir = cert_loc
214 conn.cert_reqs = 'CERT_NONE'
216 conn.ca_cert_dir = None
219 if not isinstance(cert, basestring):
220 conn.cert_file = cert[0]
221 conn.key_file = cert[1]
223 conn.cert_file = cert
225 def build_response(self, req, resp):
226 """Builds a :class:`Response <requests.Response>` object from a urllib3
227 response. This should not be called from user code, and is only exposed
228 for use when subclassing the
229 :class:`HTTPAdapter <requests.adapters.HTTPAdapter>`
231 :param req: The :class:`PreparedRequest <PreparedRequest>` used to generate the response.
232 :param resp: The urllib3 response object.
234 response = Response()
236 # Fallback to None if there's no status_code, for whatever reason.
237 response.status_code = getattr(resp, 'status', None)
239 # Make headers case-insensitive.
240 response.headers = CaseInsensitiveDict(getattr(resp, 'headers', {}))
243 response.encoding = get_encoding_from_headers(response.headers)
245 response.reason = response.raw.reason
247 if isinstance(req.url, bytes):
248 response.url = req.url.decode('utf-8')
250 response.url = req.url
252 # Add new cookies from the server.
253 extract_cookies_to_jar(response.cookies, req, resp)
255 # Give the Response some context.
256 response.request = req
257 response.connection = self
261 def get_connection(self, url, proxies=None):
262 """Returns a urllib3 connection for the given URL. This should not be
263 called from user code, and is only exposed for use when subclassing the
264 :class:`HTTPAdapter <requests.adapters.HTTPAdapter>`.
266 :param url: The URL to connect to.
267 :param proxies: (optional) A Requests-style dictionary of proxies used on this request.
269 proxy = select_proxy(url, proxies)
272 proxy = prepend_scheme_if_needed(proxy, 'http')
273 proxy_manager = self.proxy_manager_for(proxy)
274 conn = proxy_manager.connection_from_url(url)
276 # Only scheme should be lower case
277 parsed = urlparse(url)
278 url = parsed.geturl()
279 conn = self.poolmanager.connection_from_url(url)
284 """Disposes of any internal state.
286 Currently, this closes the PoolManager and any active ProxyManager,
287 which closes any pooled connections.
289 self.poolmanager.clear()
290 for proxy in self.proxy_manager.values():
293 def request_url(self, request, proxies):
294 """Obtain the url to use when making the final request.
296 If the message is being sent through a HTTP proxy, the full URL has to
297 be used. Otherwise, we should only use the path portion of the URL.
299 This should not be called from user code, and is only exposed for use
301 :class:`HTTPAdapter <requests.adapters.HTTPAdapter>`.
303 :param request: The :class:`PreparedRequest <PreparedRequest>` being sent.
304 :param proxies: A dictionary of schemes or schemes and hosts to proxy URLs.
306 proxy = select_proxy(request.url, proxies)
307 scheme = urlparse(request.url).scheme
309 is_proxied_http_request = (proxy and scheme != 'https')
310 using_socks_proxy = False
312 proxy_scheme = urlparse(proxy).scheme.lower()
313 using_socks_proxy = proxy_scheme.startswith('socks')
315 url = request.path_url
316 if is_proxied_http_request and not using_socks_proxy:
317 url = urldefragauth(request.url)
321 def add_headers(self, request, **kwargs):
322 """Add any headers needed by the connection. As of v2.0 this does
323 nothing by default, but is left for overriding by users that subclass
324 the :class:`HTTPAdapter <requests.adapters.HTTPAdapter>`.
326 This should not be called from user code, and is only exposed for use
328 :class:`HTTPAdapter <requests.adapters.HTTPAdapter>`.
330 :param request: The :class:`PreparedRequest <PreparedRequest>` to add headers to.
331 :param kwargs: The keyword arguments from the call to send().
335 def proxy_headers(self, proxy):
336 """Returns a dictionary of the headers to add to any request sent
337 through a proxy. This works with urllib3 magic to ensure that they are
338 correctly sent to the proxy, rather than in a tunnelled request if
339 CONNECT is being used.
341 This should not be called from user code, and is only exposed for use
343 :class:`HTTPAdapter <requests.adapters.HTTPAdapter>`.
345 :param proxies: The url of the proxy being used for this request.
348 username, password = get_auth_from_url(proxy)
350 if username and password:
351 headers['Proxy-Authorization'] = _basic_auth_str(username,
356 def send(self, request, stream=False, timeout=None, verify=True, cert=None, proxies=None):
357 """Sends PreparedRequest object. Returns Response object.
359 :param request: The :class:`PreparedRequest <PreparedRequest>` being sent.
360 :param stream: (optional) Whether to stream the request content.
361 :param timeout: (optional) How long to wait for the server to send
362 data before giving up, as a float, or a :ref:`(connect timeout,
363 read timeout) <timeouts>` tuple.
364 :type timeout: float or tuple
365 :param verify: (optional) Whether to verify SSL certificates.
366 :param cert: (optional) Any user-provided SSL certificate to be trusted.
367 :param proxies: (optional) The proxies dictionary to apply to the request.
370 conn = self.get_connection(request.url, proxies)
372 self.cert_verify(conn, request.url, verify, cert)
373 url = self.request_url(request, proxies)
374 self.add_headers(request)
376 chunked = not (request.body is None or 'Content-Length' in request.headers)
378 if isinstance(timeout, tuple):
380 connect, read = timeout
381 timeout = TimeoutSauce(connect=connect, read=read)
382 except ValueError as e:
383 # this may raise a string formatting error.
384 err = ("Invalid timeout {0}. Pass a (connect, read) "
385 "timeout tuple, or a single float to set "
386 "both timeouts to the same value".format(timeout))
387 raise ValueError(err)
389 timeout = TimeoutSauce(connect=timeout, read=timeout)
394 method=request.method,
397 headers=request.headers,
399 assert_same_host=False,
400 preload_content=False,
401 decode_content=False,
402 retries=self.max_retries,
408 if hasattr(conn, 'proxy_pool'):
409 conn = conn.proxy_pool
411 low_conn = conn._get_conn(timeout=DEFAULT_POOL_TIMEOUT)
414 low_conn.putrequest(request.method,
416 skip_accept_encoding=True)
418 for header, value in request.headers.items():
419 low_conn.putheader(header, value)
421 low_conn.endheaders()
423 for i in request.body:
424 low_conn.send(hex(len(i))[2:].encode('utf-8'))
425 low_conn.send(b'\r\n')
427 low_conn.send(b'\r\n')
428 low_conn.send(b'0\r\n\r\n')
430 # Receive the response from the server
432 # For Python 2.7+ versions, use buffering of HTTP
434 r = low_conn.getresponse(buffering=True)
436 # For compatibility with Python 2.6 versions and back
437 r = low_conn.getresponse()
439 resp = HTTPResponse.from_httplib(
443 preload_content=False,
447 # If we hit any problems here, clean up the connection.
448 # Then, reraise so that we can handle the actual exception.
452 except (ProtocolError, socket.error) as err:
453 raise ConnectionError(err, request=request)
455 except MaxRetryError as e:
456 if isinstance(e.reason, ConnectTimeoutError):
457 # TODO: Remove this in 3.0.0: see #2811
458 if not isinstance(e.reason, NewConnectionError):
459 raise ConnectTimeout(e, request=request)
461 if isinstance(e.reason, ResponseError):
462 raise RetryError(e, request=request)
464 if isinstance(e.reason, _ProxyError):
465 raise ProxyError(e, request=request)
467 raise ConnectionError(e, request=request)
469 except ClosedPoolError as e:
470 raise ConnectionError(e, request=request)
472 except _ProxyError as e:
475 except (_SSLError, _HTTPError) as e:
476 if isinstance(e, _SSLError):
477 raise SSLError(e, request=request)
478 elif isinstance(e, ReadTimeoutError):
479 raise ReadTimeout(e, request=request)
483 return self.build_response(request, resp)