comparison venv/lib/python2.7/site-packages/requests/packages/urllib3/poolmanager.py @ 0:d67268158946 draft

planemo upload commit a3f181f5f126803c654b3a66dd4e83a48f7e203b
author bcclaywell
date Mon, 12 Oct 2015 17:43:33 -0400
parents
children
comparison
equal deleted inserted replaced
-1:000000000000 0:d67268158946
1 import logging
2
3 try: # Python 3
4 from urllib.parse import urljoin
5 except ImportError:
6 from urlparse import urljoin
7
8 from ._collections import RecentlyUsedContainer
9 from .connectionpool import HTTPConnectionPool, HTTPSConnectionPool
10 from .connectionpool import port_by_scheme
11 from .exceptions import LocationValueError, MaxRetryError, ProxySchemeUnknown
12 from .request import RequestMethods
13 from .util.url import parse_url
14 from .util.retry import Retry
15
16
17 __all__ = ['PoolManager', 'ProxyManager', 'proxy_from_url']
18
19
20 pool_classes_by_scheme = {
21 'http': HTTPConnectionPool,
22 'https': HTTPSConnectionPool,
23 }
24
25 log = logging.getLogger(__name__)
26
27 SSL_KEYWORDS = ('key_file', 'cert_file', 'cert_reqs', 'ca_certs',
28 'ssl_version')
29
30
31 class PoolManager(RequestMethods):
32 """
33 Allows for arbitrary requests while transparently keeping track of
34 necessary connection pools for you.
35
36 :param num_pools:
37 Number of connection pools to cache before discarding the least
38 recently used pool.
39
40 :param headers:
41 Headers to include with all requests, unless other headers are given
42 explicitly.
43
44 :param \**connection_pool_kw:
45 Additional parameters are used to create fresh
46 :class:`urllib3.connectionpool.ConnectionPool` instances.
47
48 Example::
49
50 >>> manager = PoolManager(num_pools=2)
51 >>> r = manager.request('GET', 'http://google.com/')
52 >>> r = manager.request('GET', 'http://google.com/mail')
53 >>> r = manager.request('GET', 'http://yahoo.com/')
54 >>> len(manager.pools)
55 2
56
57 """
58
59 proxy = None
60
61 def __init__(self, num_pools=10, headers=None, **connection_pool_kw):
62 RequestMethods.__init__(self, headers)
63 self.connection_pool_kw = connection_pool_kw
64 self.pools = RecentlyUsedContainer(num_pools,
65 dispose_func=lambda p: p.close())
66
67 def __enter__(self):
68 return self
69
70 def __exit__(self, exc_type, exc_val, exc_tb):
71 self.clear()
72 # Return False to re-raise any potential exceptions
73 return False
74
75 def _new_pool(self, scheme, host, port):
76 """
77 Create a new :class:`ConnectionPool` based on host, port and scheme.
78
79 This method is used to actually create the connection pools handed out
80 by :meth:`connection_from_url` and companion methods. It is intended
81 to be overridden for customization.
82 """
83 pool_cls = pool_classes_by_scheme[scheme]
84 kwargs = self.connection_pool_kw
85 if scheme == 'http':
86 kwargs = self.connection_pool_kw.copy()
87 for kw in SSL_KEYWORDS:
88 kwargs.pop(kw, None)
89
90 return pool_cls(host, port, **kwargs)
91
92 def clear(self):
93 """
94 Empty our store of pools and direct them all to close.
95
96 This will not affect in-flight connections, but they will not be
97 re-used after completion.
98 """
99 self.pools.clear()
100
101 def connection_from_host(self, host, port=None, scheme='http'):
102 """
103 Get a :class:`ConnectionPool` based on the host, port, and scheme.
104
105 If ``port`` isn't given, it will be derived from the ``scheme`` using
106 ``urllib3.connectionpool.port_by_scheme``.
107 """
108
109 if not host:
110 raise LocationValueError("No host specified.")
111
112 scheme = scheme or 'http'
113 port = port or port_by_scheme.get(scheme, 80)
114 pool_key = (scheme, host, port)
115
116 with self.pools.lock:
117 # If the scheme, host, or port doesn't match existing open
118 # connections, open a new ConnectionPool.
119 pool = self.pools.get(pool_key)
120 if pool:
121 return pool
122
123 # Make a fresh ConnectionPool of the desired type
124 pool = self._new_pool(scheme, host, port)
125 self.pools[pool_key] = pool
126
127 return pool
128
129 def connection_from_url(self, url):
130 """
131 Similar to :func:`urllib3.connectionpool.connection_from_url` but
132 doesn't pass any additional parameters to the
133 :class:`urllib3.connectionpool.ConnectionPool` constructor.
134
135 Additional parameters are taken from the :class:`.PoolManager`
136 constructor.
137 """
138 u = parse_url(url)
139 return self.connection_from_host(u.host, port=u.port, scheme=u.scheme)
140
141 def urlopen(self, method, url, redirect=True, **kw):
142 """
143 Same as :meth:`urllib3.connectionpool.HTTPConnectionPool.urlopen`
144 with custom cross-host redirect logic and only sends the request-uri
145 portion of the ``url``.
146
147 The given ``url`` parameter must be absolute, such that an appropriate
148 :class:`urllib3.connectionpool.ConnectionPool` can be chosen for it.
149 """
150 u = parse_url(url)
151 conn = self.connection_from_host(u.host, port=u.port, scheme=u.scheme)
152
153 kw['assert_same_host'] = False
154 kw['redirect'] = False
155 if 'headers' not in kw:
156 kw['headers'] = self.headers
157
158 if self.proxy is not None and u.scheme == "http":
159 response = conn.urlopen(method, url, **kw)
160 else:
161 response = conn.urlopen(method, u.request_uri, **kw)
162
163 redirect_location = redirect and response.get_redirect_location()
164 if not redirect_location:
165 return response
166
167 # Support relative URLs for redirecting.
168 redirect_location = urljoin(url, redirect_location)
169
170 # RFC 7231, Section 6.4.4
171 if response.status == 303:
172 method = 'GET'
173
174 retries = kw.get('retries')
175 if not isinstance(retries, Retry):
176 retries = Retry.from_int(retries, redirect=redirect)
177
178 try:
179 retries = retries.increment(method, url, response=response, _pool=conn)
180 except MaxRetryError:
181 if retries.raise_on_redirect:
182 raise
183 return response
184
185 kw['retries'] = retries
186 kw['redirect'] = redirect
187
188 log.info("Redirecting %s -> %s" % (url, redirect_location))
189 return self.urlopen(method, redirect_location, **kw)
190
191
192 class ProxyManager(PoolManager):
193 """
194 Behaves just like :class:`PoolManager`, but sends all requests through
195 the defined proxy, using the CONNECT method for HTTPS URLs.
196
197 :param proxy_url:
198 The URL of the proxy to be used.
199
200 :param proxy_headers:
201 A dictionary contaning headers that will be sent to the proxy. In case
202 of HTTP they are being sent with each request, while in the
203 HTTPS/CONNECT case they are sent only once. Could be used for proxy
204 authentication.
205
206 Example:
207 >>> proxy = urllib3.ProxyManager('http://localhost:3128/')
208 >>> r1 = proxy.request('GET', 'http://google.com/')
209 >>> r2 = proxy.request('GET', 'http://httpbin.org/')
210 >>> len(proxy.pools)
211 1
212 >>> r3 = proxy.request('GET', 'https://httpbin.org/')
213 >>> r4 = proxy.request('GET', 'https://twitter.com/')
214 >>> len(proxy.pools)
215 3
216
217 """
218
219 def __init__(self, proxy_url, num_pools=10, headers=None,
220 proxy_headers=None, **connection_pool_kw):
221
222 if isinstance(proxy_url, HTTPConnectionPool):
223 proxy_url = '%s://%s:%i' % (proxy_url.scheme, proxy_url.host,
224 proxy_url.port)
225 proxy = parse_url(proxy_url)
226 if not proxy.port:
227 port = port_by_scheme.get(proxy.scheme, 80)
228 proxy = proxy._replace(port=port)
229
230 if proxy.scheme not in ("http", "https"):
231 raise ProxySchemeUnknown(proxy.scheme)
232
233 self.proxy = proxy
234 self.proxy_headers = proxy_headers or {}
235
236 connection_pool_kw['_proxy'] = self.proxy
237 connection_pool_kw['_proxy_headers'] = self.proxy_headers
238
239 super(ProxyManager, self).__init__(
240 num_pools, headers, **connection_pool_kw)
241
242 def connection_from_host(self, host, port=None, scheme='http'):
243 if scheme == "https":
244 return super(ProxyManager, self).connection_from_host(
245 host, port, scheme)
246
247 return super(ProxyManager, self).connection_from_host(
248 self.proxy.host, self.proxy.port, self.proxy.scheme)
249
250 def _set_proxy_headers(self, url, headers=None):
251 """
252 Sets headers needed by proxies: specifically, the Accept and Host
253 headers. Only sets headers not provided by the user.
254 """
255 headers_ = {'Accept': '*/*'}
256
257 netloc = parse_url(url).netloc
258 if netloc:
259 headers_['Host'] = netloc
260
261 if headers:
262 headers_.update(headers)
263 return headers_
264
265 def urlopen(self, method, url, redirect=True, **kw):
266 "Same as HTTP(S)ConnectionPool.urlopen, ``url`` must be absolute."
267 u = parse_url(url)
268
269 if u.scheme == "http":
270 # For proxied HTTPS requests, httplib sets the necessary headers
271 # on the CONNECT to the proxy. For HTTP, we'll definitely
272 # need to set 'Host' at the very least.
273 headers = kw.get('headers', self.headers)
274 kw['headers'] = self._set_proxy_headers(url, headers)
275
276 return super(ProxyManager, self).urlopen(method, url, redirect=redirect, **kw)
277
278
279 def proxy_from_url(url, **kw):
280 return ProxyManager(proxy_url=url, **kw)