1
1
# urllib3/connectionpool.py
2
# Copyright 2008-2012 Andrey Petrov and contributors (see CONTRIBUTORS.txt)
2
# Copyright 2008-2013 Andrey Petrov and contributors (see CONTRIBUTORS.txt)
4
4
# This module is part of urllib3 and is released under
5
5
# the MIT License: http://www.opensource.org/licenses/mit-license.php
10
11
from socket import error as SocketError, timeout as SocketTimeout
12
from .util import resolve_cert_reqs, resolve_ssl_version, assert_fingerprint
13
15
from http.client import HTTPConnection, HTTPException
14
16
from http.client import HTTP_PORT, HTTPS_PORT
15
17
except ImportError:
16
18
from httplib import HTTPConnection, HTTPException
17
19
from httplib import HTTP_PORT, HTTPS_PORT
20
22
from queue import LifoQueue, Empty, Full
21
23
except ImportError:
22
24
from Queue import LifoQueue, Empty, Full
25
try: # Compiled with SSL?
27
try: # Compiled with SSL?
26
28
HTTPSConnection = object
27
29
BaseSSLError = None
31
33
from http.client import HTTPSConnection
32
34
except ImportError:
33
35
from httplib import HTTPSConnection
36
38
BaseSSLError = ssl.SSLError
38
except (ImportError, AttributeError):
40
except (ImportError, AttributeError): # Platform-specific: No SSL.
42
44
from .request import RequestMethods
43
45
from .response import HTTPResponse
44
from .util import get_host, is_connection_dropped
46
from .util import get_host, is_connection_dropped, ssl_wrap_socket
45
47
from .exceptions import (
79
83
def set_cert(self, key_file=None, cert_file=None,
80
cert_reqs='CERT_NONE', ca_certs=None):
82
'CERT_NONE': ssl.CERT_NONE,
83
'CERT_OPTIONAL': ssl.CERT_OPTIONAL,
84
'CERT_REQUIRED': ssl.CERT_REQUIRED
84
cert_reqs=None, ca_certs=None,
85
assert_hostname=None, assert_fingerprint=None):
87
87
self.key_file = key_file
88
88
self.cert_file = cert_file
89
self.cert_reqs = ssl_req_scheme.get(cert_reqs) or ssl.CERT_NONE
89
self.cert_reqs = cert_reqs
90
90
self.ca_certs = ca_certs
91
self.assert_hostname = assert_hostname
92
self.assert_fingerprint = assert_fingerprint
93
95
# Add certificate verification
94
96
sock = socket.create_connection((self.host, self.port), self.timeout)
98
resolved_cert_reqs = resolve_cert_reqs(self.cert_reqs)
99
resolved_ssl_version = resolve_ssl_version(self.ssl_version)
96
101
# Wrap socket using verification with the root certs in
97
102
# trusted_root_certs
98
self.sock = ssl.wrap_socket(sock, self.key_file, self.cert_file,
99
cert_reqs=self.cert_reqs,
100
ca_certs=self.ca_certs)
102
match_hostname(self.sock.getpeercert(), self.host)
103
self.sock = ssl_wrap_socket(sock, self.key_file, self.cert_file,
104
cert_reqs=resolved_cert_reqs,
105
ca_certs=self.ca_certs,
106
server_hostname=self.host,
107
ssl_version=resolved_ssl_version)
109
if resolved_cert_reqs != ssl.CERT_NONE:
110
if self.assert_fingerprint:
111
assert_fingerprint(self.sock.getpeercert(binary_form=True),
112
self.assert_fingerprint)
114
match_hostname(self.sock.getpeercert(),
115
self.assert_hostname or self.host)
166
178
def __init__(self, host, port=None, strict=False, timeout=None, maxsize=1,
167
179
block=False, headers=None):
168
super(HTTPConnectionPool, self).__init__(host, port)
180
ConnectionPool.__init__(self, host, port)
181
RequestMethods.__init__(self, headers)
170
183
self.strict = strict
171
184
self.timeout = timeout
172
185
self.pool = self.QueueCls(maxsize)
173
186
self.block = block
174
self.headers = headers or {}
176
188
# Fill the queue up so that doing get() on it will block properly
177
189
for _ in xrange(maxsize):
188
200
self.num_connections += 1
189
201
log.info("Starting new HTTP connection (%d): %s" %
190
202
(self.num_connections, self.host))
191
return HTTPConnection(host=self.host, port=self.port)
203
return HTTPConnection(host=self.host,
193
207
def _get_conn(self, timeout=None):
207
221
conn = self.pool.get(block=self.block, timeout=timeout)
209
# If this is a persistent connection, check if it got disconnected
210
if conn and is_connection_dropped(conn):
211
log.info("Resetting dropped connection: %s" % self.host)
223
except AttributeError: # self.pool is None
224
raise ClosedPoolError(self, "Pool is closed.")
218
230
"connections are allowed.")
219
231
pass # Oh well, we'll create a new connection then
233
# If this is a persistent connection, check if it got disconnected
234
if conn and is_connection_dropped(conn):
235
log.info("Resetting dropped connection: %s" % self.host)
221
238
return conn or self._new_conn()
223
240
def _put_conn(self, conn):
228
245
Connection object for the current host and port as returned by
229
246
:meth:`._new_conn` or :meth:`._get_conn`.
231
If the pool is already full, the connection is discarded because we
232
exceeded maxsize. If connections are discarded frequently, then maxsize
248
If the pool is already full, the connection is closed and discarded
249
because we exceeded maxsize. If connections are discarded frequently,
250
then maxsize should be increased.
252
If the pool is closed, then the connection will be closed and discarded.
236
255
self.pool.put(conn, block=False)
256
return # Everything is dandy, done.
257
except AttributeError:
238
261
# This should never happen if self.block == True
239
262
log.warning("HttpConnectionPool is full, discarding connection: %s"
265
# Connection never got put back into the pool, close it.
242
268
def _make_request(self, conn, method, url, timeout=_Default,
243
269
**httplib_request_kw):
259
285
sock.settimeout(timeout)
261
httplib_response = conn.getresponse()
263
log.debug("\"%s %s %s\" %s %s" %
265
conn._http_vsn_str, # pylint: disable-msg=W0212
266
httplib_response.status, httplib_response.length))
287
try: # Python 2.7+, use buffering of HTTP responses
288
httplib_response = conn.getresponse(buffering=True)
289
except TypeError: # Python 2.6 and older
290
httplib_response = conn.getresponse()
292
# AppEngine doesn't have a version attr.
293
http_version = getattr(conn, '_http_vsn_str', 'HTTP/?')
294
log.debug("\"%s %s %s\" %s %s" % (method, url, http_version,
295
httplib_response.status,
296
httplib_response.length))
268
297
return httplib_response
301
Close all pooled connections and disable the pool.
303
# Disable access to the pool
304
old_pool, self.pool = self.pool, None
308
conn = old_pool.get(block=False)
271
315
def is_same_host(self, url):
273
317
Check if the given ``url`` is a member of the same host as this
320
if url.startswith('/'):
276
323
# TODO: Add optional support for socket.gethostbyname checking.
277
324
scheme, host, port = get_host(url)
280
327
# Use explicit default port for comparison when none is given.
281
328
port = port_by_scheme.get(scheme)
283
return (url.startswith('/') or
284
(scheme, host, port) == (self.scheme, self.host, self.port))
330
return (scheme, host, port) == (self.scheme, self.host, self.port)
286
332
def urlopen(self, method, url, body=None, headers=None, retries=3,
287
333
redirect=True, assert_same_host=True, timeout=_Default,
320
366
Number of retries to allow before raising a MaxRetryError exception.
323
Automatically handle redirects (status codes 301, 302, 303, 307),
324
each redirect counts as a retry.
369
If True, automatically handle redirects (status codes 301, 302,
370
303, 307). Each redirect counts as a retry.
326
372
:param assert_same_host:
327
373
If ``True``, will make sure that the host of the pool requests is
423
468
# This is necessary so we can access e below
472
raise MaxRetryError(self, url, e)
427
if conn and release_conn:
428
# Put the connection back to be reused
476
# Put the connection back to be reused. If the connection is
477
# expired then it will be None, which will get replaced with a
478
# fresh connection during _get_conn.
429
479
self._put_conn(conn)
432
483
log.warn("Retrying (%d attempts remain) after connection "
433
484
"broken by '%r': %s" % (retries, err, url))
434
485
return self.urlopen(method, url, body, headers, retries - 1,
435
redirect, assert_same_host) # Try again
486
redirect, assert_same_host,
487
timeout=timeout, pool_timeout=pool_timeout,
488
release_conn=release_conn, **response_kw)
437
490
# Handle redirect?
438
491
redirect_location = redirect and response.get_redirect_location()
439
492
if redirect_location:
493
if response.status == 303:
440
495
log.info("Redirecting %s -> %s" % (url, redirect_location))
441
496
return self.urlopen(method, redirect_location, body, headers,
442
retries - 1, redirect, assert_same_host)
497
retries - 1, redirect, assert_same_host,
498
timeout=timeout, pool_timeout=pool_timeout,
499
release_conn=release_conn, **response_kw)
451
508
When Python is compiled with the :mod:`ssl` module, then
452
509
:class:`.VerifiedHTTPSConnection` is used, which *can* verify certificates,
453
instead of :class:httplib.HTTPSConnection`.
455
The ``key_file``, ``cert_file``, ``cert_reqs``, and ``ca_certs`` parameters
456
are only used if :mod:`ssl` is available and are fed into
457
:meth:`ssl.wrap_socket` to upgrade the connection socket into an SSL socket.
510
instead of :class:`httplib.HTTPSConnection`.
512
:class:`.VerifiedHTTPSConnection` uses one of ``assert_fingerprint``,
513
``assert_hostname`` and ``host`` in this order to verify connections.
515
The ``key_file``, ``cert_file``, ``cert_reqs``, ``ca_certs`` and
516
``ssl_version`` are only used if :mod:`ssl` is available and are fed into
517
:meth:`urllib3.util.ssl_wrap_socket` to upgrade the connection socket
462
523
def __init__(self, host, port=None,
463
524
strict=False, timeout=None, maxsize=1,
464
525
block=False, headers=None,
465
key_file=None, cert_file=None,
466
cert_reqs='CERT_NONE', ca_certs=None):
526
key_file=None, cert_file=None, cert_reqs=None,
527
ca_certs=None, ssl_version=None,
528
assert_hostname=None, assert_fingerprint=None):
468
super(HTTPSConnectionPool, self).__init__(host, port,
469
strict, timeout, maxsize,
530
HTTPConnectionPool.__init__(self, host, port,
531
strict, timeout, maxsize,
471
533
self.key_file = key_file
472
534
self.cert_file = cert_file
473
535
self.cert_reqs = cert_reqs
474
536
self.ca_certs = ca_certs
537
self.ssl_version = ssl_version
538
self.assert_hostname = assert_hostname
539
self.assert_fingerprint = assert_fingerprint
476
541
def _new_conn(self):
481
546
log.info("Starting new HTTPS connection (%d): %s"
482
547
% (self.num_connections, self.host))
484
if not ssl: # Platform-specific: Python compiled without +ssl
549
if not ssl: # Platform-specific: Python compiled without +ssl
485
550
if not HTTPSConnection or HTTPSConnection is object:
486
551
raise SSLError("Can't connect to HTTPS URL because the SSL "
487
552
"module is not available.")
489
return HTTPSConnection(host=self.host, port=self.port)
554
return HTTPSConnection(host=self.host,
491
connection = VerifiedHTTPSConnection(host=self.host, port=self.port)
558
connection = VerifiedHTTPSConnection(host=self.host,
492
561
connection.set_cert(key_file=self.key_file, cert_file=self.cert_file,
493
cert_reqs=self.cert_reqs, ca_certs=self.ca_certs)
562
cert_reqs=self.cert_reqs, ca_certs=self.ca_certs,
563
assert_hostname=self.assert_hostname,
564
assert_fingerprint=self.assert_fingerprint)
566
connection.ssl_version = self.ssl_version
494
568
return connection