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
This commit is contained in:
Ian Cordasco
2014-12-08 15:28:04 -06:00
parent 521cc25a0a
commit 7ee96cbe39
2 changed files with 31 additions and 14 deletions

View File

@@ -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]

View File

@@ -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))