aboutsummaryrefslogtreecommitdiff
path: root/requests/adapters.py
diff options
context:
space:
mode:
Diffstat (limited to 'requests/adapters.py')
-rw-r--r--requests/adapters.py224
1 files changed, 224 insertions, 0 deletions
diff --git a/requests/adapters.py b/requests/adapters.py
new file mode 100644
index 0000000..5f9d9c7
--- /dev/null
+++ b/requests/adapters.py
@@ -0,0 +1,224 @@
+# -*- coding: utf-8 -*-
+
+"""
+requests.adapters
+~~~~~~~~~~~~~~~~~
+
+This module contains the transport adapters that Requests uses to define
+and maintain connections.
+"""
+
+import socket
+
+from .models import Response
+from .packages.urllib3.poolmanager import PoolManager, proxy_from_url
+from .packages.urllib3.response import HTTPResponse
+from .hooks import dispatch_hook
+from .compat import urlparse, basestring, urldefrag
+from .utils import (DEFAULT_CA_BUNDLE_PATH, get_encoding_from_headers,
+ prepend_scheme_if_needed)
+from .structures import CaseInsensitiveDict
+from .packages.urllib3.exceptions import MaxRetryError
+from .packages.urllib3.exceptions import TimeoutError
+from .packages.urllib3.exceptions import SSLError as _SSLError
+from .packages.urllib3.exceptions import HTTPError as _HTTPError
+from .cookies import extract_cookies_to_jar
+from .exceptions import ConnectionError, Timeout, SSLError
+
+DEFAULT_POOLSIZE = 10
+DEFAULT_RETRIES = 0
+
+
+class BaseAdapter(object):
+ """The Base Transport Adapter"""
+
+ def __init__(self):
+ super(BaseAdapter, self).__init__()
+
+ def send(self):
+ raise NotImplementedError
+
+ def close(self):
+ raise NotImplementedError
+
+
+class HTTPAdapter(BaseAdapter):
+ """Built-In HTTP Adapter for Urllib3."""
+ def __init__(self, pool_connections=DEFAULT_POOLSIZE, pool_maxsize=DEFAULT_POOLSIZE):
+ self.max_retries = DEFAULT_RETRIES
+ self.config = {}
+
+ super(HTTPAdapter, self).__init__()
+
+ self.init_poolmanager(pool_connections, pool_maxsize)
+
+ def init_poolmanager(self, connections, maxsize):
+ self.poolmanager = PoolManager(num_pools=connections, maxsize=maxsize)
+
+ def cert_verify(self, conn, url, verify, cert):
+ if url.startswith('https') and verify:
+
+ cert_loc = None
+
+ # Allow self-specified cert location.
+ if verify is not True:
+ cert_loc = verify
+
+ if not cert_loc:
+ cert_loc = DEFAULT_CA_BUNDLE_PATH
+
+ if not cert_loc:
+ raise Exception("Could not find a suitable SSL CA certificate bundle.")
+
+ conn.cert_reqs = 'CERT_REQUIRED'
+ conn.ca_certs = cert_loc
+ else:
+ conn.cert_reqs = 'CERT_NONE'
+ conn.ca_certs = None
+
+ if cert:
+ if not isinstance(cert, basestring):
+ conn.cert_file = cert[0]
+ conn.key_file = cert[1]
+ else:
+ conn.cert_file = cert
+
+ def build_response(self, req, resp):
+ response = Response()
+
+ # Fallback to None if there's no status_code, for whatever reason.
+ response.status_code = getattr(resp, 'status', None)
+
+ # Make headers case-insensitive.
+ response.headers = CaseInsensitiveDict(getattr(resp, 'headers', {}))
+
+ # Set encoding.
+ response.encoding = get_encoding_from_headers(response.headers)
+ response.raw = resp
+ response.reason = response.raw.reason
+
+ if isinstance(req.url, bytes):
+ response.url = req.url.decode('utf-8')
+ else:
+ response.url = req.url
+
+ # Add new cookies from the server.
+ extract_cookies_to_jar(response.cookies, req, resp)
+
+ # Give the Response some context.
+ response.request = req
+ response.connection = self
+
+ # Run the Response hook.
+ response = dispatch_hook('response', req.hooks, response)
+ return response
+
+ def get_connection(self, url, proxies=None):
+ """Returns a connection for the given URL."""
+ proxies = proxies or {}
+ proxy = proxies.get(urlparse(url).scheme)
+
+ if proxy:
+ proxy = prepend_scheme_if_needed(proxy, urlparse(url).scheme)
+ conn = proxy_from_url(proxy)
+ else:
+ conn = self.poolmanager.connection_from_url(url)
+
+ return conn
+
+ def close(self):
+ """Dispose of any internal state.
+
+ Currently, this just closes the PoolManager, which closes pooled
+ connections.
+ """
+ self.poolmanager.clear()
+
+ def request_url(self, request, proxies):
+ """Obtain the url to use when making the final request.
+
+ If the message is being sent through a proxy, the full URL has to be
+ used. Otherwise, we should only use the path portion of the URL."""
+ proxies = proxies or {}
+ proxy = proxies.get(urlparse(request.url).scheme)
+
+ if proxy:
+ url, _ = urldefrag(request.url)
+ else:
+ url = request.path_url
+
+ return url
+
+ def send(self, request, stream=False, timeout=None, verify=True, cert=None, proxies=None):
+ """Sends PreparedRequest object. Returns Response object."""
+
+ conn = self.get_connection(request.url, proxies)
+
+ self.cert_verify(conn, request.url, verify, cert)
+ url = self.request_url(request, proxies)
+
+ chunked = not (request.body is None or 'Content-Length' in request.headers)
+
+ try:
+ if not chunked:
+ resp = conn.urlopen(
+ method=request.method,
+ url=url,
+ body=request.body,
+ headers=request.headers,
+ redirect=False,
+ assert_same_host=False,
+ preload_content=False,
+ decode_content=False,
+ retries=self.max_retries,
+ timeout=timeout
+ )
+
+ # Send the request.
+ else:
+ if hasattr(conn, 'proxy_pool'):
+ conn = conn.proxy_pool
+
+ low_conn = conn._get_conn(timeout=timeout)
+ low_conn.putrequest(request.method, url, skip_accept_encoding=True)
+
+ for header, value in request.headers.items():
+ low_conn.putheader(header, value)
+
+ low_conn.endheaders()
+
+ for i in request.body:
+ low_conn.send(hex(len(i))[2:].encode('utf-8'))
+ low_conn.send(b'\r\n')
+ low_conn.send(i)
+ low_conn.send(b'\r\n')
+ low_conn.send(b'0\r\n\r\n')
+
+ r = low_conn.getresponse()
+ resp = HTTPResponse.from_httplib(r,
+ pool=conn,
+ connection=low_conn,
+ preload_content=False,
+ decode_content=False
+ )
+
+ except socket.error as sockerr:
+ raise ConnectionError(sockerr)
+
+ except MaxRetryError as e:
+ raise ConnectionError(e)
+
+ except (_SSLError, _HTTPError) as e:
+ if isinstance(e, _SSLError):
+ raise SSLError(e)
+ elif isinstance(e, TimeoutError):
+ raise Timeout(e)
+ else:
+ raise Timeout('Request timed out.')
+
+ r = self.build_response(request, resp)
+
+ if not stream:
+ r.content
+
+ return r