From 7ee96cbe390b2492f8d837c93f33a8f5bebdb388 Mon Sep 17 00:00:00 2001 From: Ian Cordasco Date: Mon, 8 Dec 2014 15:28:04 -0600 Subject: [PATCH] Register our own ConnectionPool without globals Currently, on systems like Fedora and Debian, it is possible to import urllib3 as well as requests.packages.urllib3. They functionally point to the same code but sys.modules considers them to be separate items. When downstream packagers unvendor urllib3 from requests, they also change all the imports inside of the package. So if the code imports urllib3 from requests.packages.urllib3 and modifies globals in a submodule, that will not be visible to requests since it has been rewritten to use urllib3 (not requests.packages.urllib3). By handling this logic ourselves, we can issue a release until upstream packages and requests can fix this and cut a new release. Change-Id: Ic77ce8a06d9d148a899b4b8695990fca8fdaefc5 Closes-bug: 1396550 --- glanceclient/common/https.py | 37 ++++++++++++++++++++++++++---------- tests/test_ssl.py | 8 ++++---- 2 files changed, 31 insertions(+), 14 deletions(-) diff --git a/glanceclient/common/https.py b/glanceclient/common/https.py index 6baa6afb..fc719e2c 100644 --- a/glanceclient/common/https.py +++ b/glanceclient/common/https.py @@ -14,19 +14,18 @@ # under the License. import socket +import ssl import struct import OpenSSL from requests import adapters +from requests import compat try: from requests.packages.urllib3 import connectionpool - from requests.packages.urllib3 import poolmanager except ImportError: from urllib3 import connectionpool - from urllib3 import poolmanager import six -import ssl from glanceclient.common import utils @@ -70,19 +69,37 @@ class HTTPSAdapter(adapters.HTTPAdapter): one. """ - def __init__(self, *args, **kwargs): - # NOTE(flaper87): This line forces poolmanager to use - # glanceclient HTTPSConnection - classes_by_scheme = poolmanager.pool_classes_by_scheme - classes_by_scheme["glance+https"] = HTTPSConnectionPool - super(HTTPSAdapter, self).__init__(*args, **kwargs) - def request_url(self, request, proxies): # NOTE(flaper87): Make sure the url is encoded, otherwise # python's standard httplib will fail with a TypeError. url = super(HTTPSAdapter, self).request_url(request, proxies) return strutils.safe_encode(url) + def _create_glance_httpsconnectionpool(self, url): + kw = self.poolmanager.connection_kw + # Parse the url to get the scheme, host, and port + parsed = compat.urlparse(url) + # If there is no port specified, we should use the standard HTTPS port + port = parsed.port or 443 + pool = HTTPSConnectionPool(parsed.host, port, **kw) + + with self.poolmanager.pools.lock: + self.poolmanager.pools[(parsed.scheme, parsed.host, port)] = pool + + return pool + + def get_connection(self, url, proxies=None): + try: + return super(HTTPSAdapter, self).get_connection(url, proxies) + except KeyError: + # NOTE(sigamvirus24): This works around modifying a module global + # which fixes bug #1396550 + # The scheme is most likely glance+https but check anyway + if not url.startswith('glance+https://'): + raise + + return self._create_glance_httpsconnectionpool(url) + def cert_verify(self, conn, url, verify, cert): super(HTTPSAdapter, self).cert_verify(conn, url, verify, cert) conn.ca_certs = verify[0] diff --git a/tests/test_ssl.py b/tests/test_ssl.py index 013d18fb..c7fcc85c 100644 --- a/tests/test_ssl.py +++ b/tests/test_ssl.py @@ -16,7 +16,10 @@ import os from OpenSSL import crypto -from requests.packages.urllib3 import poolmanager +try: + from requests.packages.urllib3 import poolmanager +except ImportError: + from urllib3 import poolmanager import testtools from glanceclient.common import http @@ -48,9 +51,6 @@ class TestRequestsIntegration(testtools.TestCase): self.assertNotEqual(https.HTTPSConnectionPool, poolmanager.pool_classes_by_scheme["https"]) - self.assertEqual(https.HTTPSConnectionPool, - poolmanager.pool_classes_by_scheme["glance+https"]) - adapter = client.session.adapters.get("https://") self.assertFalse(isinstance(adapter, https.HTTPSAdapter))