Skip to content

Commit 3fb37a2

Browse files
authored
Consistent handling of HTTP/S proxy environment variables (#2079)
* Remove explicit handling of proxy env variables in favor of downstream handling through Requests library * Add unit test to verify underlying library respects proxy env vars * fix formatting errors
1 parent de03f1d commit 3fb37a2

File tree

2 files changed

+50
-23
lines changed

2 files changed

+50
-23
lines changed

libcloud/http.py

Lines changed: 3 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,6 @@
1818
verification, depending on libcloud.security settings.
1919
"""
2020

21-
import os
2221
import warnings
2322

2423
import requests
@@ -42,9 +41,6 @@
4241
# Default timeout for HTTP requests in seconds
4342
DEFAULT_REQUEST_TIMEOUT = 60
4443

45-
HTTP_PROXY_ENV_VARIABLE_NAME = "http_proxy"
46-
HTTPS_PROXY_ENV_VARIABLE_NAME = "https_proxy"
47-
4844

4945
class SignedHTTPSAdapter(HTTPAdapter):
5046
def __init__(self, cert_file, key_file):
@@ -88,6 +84,7 @@ def __init__(self):
8884
def set_http_proxy(self, proxy_url):
8985
"""
9086
Set a HTTP proxy which will be used with this connection.
87+
This will override any proxy environment variables.
9188
9289
:param proxy_url: Proxy URL (e.g. http://<hostname>:<port> without
9390
authentication and
@@ -197,13 +194,8 @@ def __init__(self, host, port, secure=None, **kwargs):
197194
":{}".format(port) if port not in (80, 443) else "",
198195
)
199196

200-
# Support for HTTP(s) proxy
201-
# NOTE: We always only use a single proxy (either HTTP or HTTPS)
202-
https_proxy_url_env = os.environ.get(HTTPS_PROXY_ENV_VARIABLE_NAME, None)
203-
http_proxy_url_env = os.environ.get(HTTP_PROXY_ENV_VARIABLE_NAME, https_proxy_url_env)
204-
205-
# Connection argument has precedence over environment variables
206-
proxy_url = kwargs.pop("proxy_url", http_proxy_url_env)
197+
# Connection argument has precedence over environment variables, which are handled downstream
198+
proxy_url = kwargs.pop("proxy_url", None)
207199

208200
self._setup_verify()
209201
self._setup_ca_cert()

libcloud/test/test_connection.py

Lines changed: 47 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121
from unittest.mock import Mock, patch
2222

2323
import requests_mock
24+
from requests.adapters import HTTPAdapter
2425
from requests.exceptions import ConnectTimeout
2526

2627
import libcloud.common.base
@@ -36,6 +37,7 @@ class BaseConnectionClassTestCase(unittest.TestCase):
3637
def setUp(self):
3738
self.orig_http_proxy = os.environ.pop("http_proxy", None)
3839
self.orig_https_proxy = os.environ.pop("https_proxy", None)
40+
self.orig_no_proxy = os.environ.pop("no_proxy", None)
3941

4042
def tearDown(self):
4143
if self.orig_http_proxy:
@@ -48,6 +50,11 @@ def tearDown(self):
4850
elif "https_proxy" in os.environ:
4951
del os.environ["https_proxy"]
5052

53+
if self.orig_no_proxy:
54+
os.environ["no_proxy"] = self.orig_no_proxy
55+
elif "no_proxy" in os.environ:
56+
del os.environ["no_proxy"]
57+
5158
libcloud.common.base.ALLOW_PATH_DOUBLE_SLASHES = False
5259

5360
def test_parse_proxy_url(self):
@@ -104,22 +111,12 @@ def test_parse_proxy_url(self):
104111
)
105112

106113
def test_constructor(self):
107-
proxy_url = "http://127.0.0.2:3128"
108-
os.environ["http_proxy"] = proxy_url
109-
conn = LibcloudConnection(host="localhost", port=80)
110-
self.assertEqual(conn.proxy_scheme, "http")
111-
self.assertEqual(conn.proxy_host, "127.0.0.2")
112-
self.assertEqual(conn.proxy_port, 3128)
113-
self.assertEqual(
114-
conn.session.proxies,
115-
{"http": "http://127.0.0.2:3128", "https": "http://127.0.0.2:3128"},
116-
)
117-
118114
_ = os.environ.pop("http_proxy", None)
119115
conn = LibcloudConnection(host="localhost", port=80)
120116
self.assertIsNone(conn.proxy_scheme)
121117
self.assertIsNone(conn.proxy_host)
122118
self.assertIsNone(conn.proxy_port)
119+
self.assertTrue(conn.session.proxies is None or not conn.session.proxies)
123120

124121
proxy_url = "http://127.0.0.3:3128"
125122
conn.set_http_proxy(proxy_url=proxy_url)
@@ -143,7 +140,8 @@ def test_constructor(self):
143140

144141
os.environ["http_proxy"] = proxy_url
145142
proxy_url = "http://127.0.0.5:3128"
146-
conn = LibcloudConnection(host="localhost", port=80, proxy_url=proxy_url)
143+
conn = LibcloudConnection(host="localhost", port=80)
144+
conn.set_http_proxy(proxy_url=proxy_url)
147145
self.assertEqual(conn.proxy_scheme, "http")
148146
self.assertEqual(conn.proxy_host, "127.0.0.5")
149147
self.assertEqual(conn.proxy_port, 3128)
@@ -163,6 +161,43 @@ def test_constructor(self):
163161
{"http": "https://127.0.0.6:3129", "https": "https://127.0.0.6:3129"},
164162
)
165163

164+
def test_proxy_environment_variables_respected(self):
165+
"""
166+
Test that proxy environment variables are respected by the underlying Requests library
167+
"""
168+
169+
def mock_send(self, request, **kwargs):
170+
captured_proxies.update(kwargs.get("proxies", {}))
171+
nonlocal captured_url
172+
captured_url = request.url
173+
mock_response = Mock()
174+
mock_response.status_code = 200
175+
mock_response.headers = {"content-type": "application/json", "location": ""}
176+
mock_response.text = "OK"
177+
mock_response.history = [] # No redirects
178+
return mock_response
179+
180+
with patch.object(HTTPAdapter, "send", mock_send):
181+
os.environ["http_proxy"] = "http://proxy.example.com:8080"
182+
os.environ["https_proxy"] = "https://secure-proxy.example.com:8443"
183+
os.environ["no_proxy"] = "localhost,127.0.0.1"
184+
captured_proxies = {}
185+
captured_url = None
186+
187+
conn = LibcloudConnection(host="localhost", port=80)
188+
conn.request("GET", "/get")
189+
190+
self.assertEqual(captured_proxies, {})
191+
self.assertIn("localhost", captured_url)
192+
193+
conn = LibcloudConnection(host="test.com", port=80)
194+
conn.request("GET", "/get")
195+
196+
self.assertEqual(captured_proxies.get("http", None), "http://proxy.example.com:8080")
197+
self.assertEqual(
198+
captured_proxies.get("https", None), "https://secure-proxy.example.com:8443"
199+
)
200+
166201
def test_connection_to_unusual_port(self):
167202
conn = LibcloudConnection(host="localhost", port=8080)
168203
self.assertIsNone(conn.proxy_scheme)

0 commit comments

Comments
 (0)