New helper method set_http_proxy in Session to set proxies in a more user friendly way

This commit is contained in:
Sylvain MARIE
2020-11-27 11:51:16 +01:00
parent 9ce669e3ae
commit 4b66260cb7
2 changed files with 120 additions and 0 deletions
+85
View File
@@ -698,6 +698,91 @@ class Session(SessionRedirectMixin):
return r
def set_http_proxy(self,
http_scheme='http', # type: str
http_host=None, # type: str
http_port=80, # type: int
http_url=None, # type: str
use_http_proxy_for_https_requests=False, # type: bool
https_scheme='https', # type: str
https_host=None, # type: str
https_port=443, # type: int
https_url=None, # type: str
replace=False # type: bool
):
"""Update or replace self.proxies with the provided proxy information.
This method updates or replaces (depending on the value of `replace`) the dictionary in `self.proxies` with the
provided information. For each kind of connection (http and https), there are two ways to pass the information:
either as an url string (`http_url`, `https_url`), or split in schema/host/port, with sensible defaults.
In addition if the exact same proxy information is to be used for http and https, you can pass only the http
one and set `use_http_proxy_for_https_requests` to True.
See :ref:`Proxies documentation <_proxies>` for details.
:param http_host: (optional) a string indicating the http proxy host, for example '10.10.1.10' or 'acme.com'.
:param http_port: (optional) an int indicating the http proxy port, for example `3128`.
:param http_scheme: (optional) a string indicating the scheme to use for http proxy. By default this is 'http'
but you can consider using 'socks5', 'socks5h'. See documentation for details.
:param http_url: (optional) a string indicating the full http proxy url. For example 'http://10.10.1.10:3128'
or 'http://user:pass@10.10.1.10:3128/' or 'socks5://user:pass@host:port'.
Only one of {http_scheme + http_host + http_port} or {http_url} should be provided.
:param use_http_proxy_for_https_requests: (optional) a boolean indicating whether the information provided for
the http proxy should be copied for the https proxy. Note that the full url will be copied including the
scheme (so by default 'http').
:param https_host: (optional) a string indicating the https proxy host, for example '10.10.1.10' or 'acme.com'.
:param https_port: (optional) an int indicating the https proxy port, for example `3128`.
:param https_scheme: (optional) a string indicating the scheme to use for https proxy. By default this is
'https' but you can consider using 'socks5', 'socks5h'. See documentation for details.
:param https_url: (optional) a string indicating the full https proxy url. For example 'https://10.10.1.10:3128'
or 'http://user:pass@10.10.1.10:3128/' or 'socks5://user:pass@host:port'.
Only one of {https_scheme + https_host + https_port} or {https_url} should be provided.
:param replace: (optional) a boolean indicating if the provided information should replace the existing one
(True) or just update it (False, default).
:return:
"""
proxies = dict()
# HTTPS
if http_host is not None:
# (a) scheme + host + port
if http_url is not None:
raise ValueError("Only one of `http_host` and `http_url` should be provided")
proxies['http'] = "%s://%s:%s" % (http_scheme, http_host, http_port)
elif http_url is not None:
# (b) full url
proxies['http'] = http_url
elif http_port != 80 or http_scheme != 'http':
raise ValueError("An `http_host` should be provided if you wish to change `http_port` or `http_scheme`")
# HTTPS
if use_http_proxy_for_https_requests:
# (a) copy the information from http
if https_host is not None or https_url is not None or https_port != 443 or https_scheme != "https":
raise ValueError("`use_http_proxy_for_https_requests` was set to `True` but custom information for "
"https was provided.")
try:
proxies['https'] = proxies['http']
except KeyError:
raise ValueError("`use_http_proxy_for_https_requests` was set to `True` but no information was "
"provided for the http proxy")
elif https_host is not None:
# (b) scheme + host + port
if https_url is not None:
raise ValueError("Only one of `https_host` and `https_url` should be provided")
proxies['https'] = '%s://%s:%s' % (https_scheme, https_host, https_port)
elif https_url is not None:
# (c) full url
proxies['https'] = https_url
elif https_port != 443 or https_scheme != 'https':
raise ValueError("An `https_host` should be provided if you wish to change `https_port` or `https_scheme`")
# Replace or update (default) the configuration
if replace:
self.proxies = proxies
else:
self.proxies.update(proxies)
def merge_environment_settings(self, url, proxies, stream, verify, cert):
"""
Check the environment and merge it with some settings.
+35
View File
@@ -151,6 +151,41 @@ class TestRequests:
request = requests.Request('GET', ' http://example.com').prepare()
assert request.url == 'http://example.com/'
def test_proxies_shortcut(self):
"""Test that the set_http_proxy helper function works correctly"""
s = requests.Session()
# nominal
s.set_http_proxy(http_scheme='socks5', http_host='acme.com', http_port=999)
assert s.proxies == {'http': 'socks5://acme.com:999'}
s.set_http_proxy(https_scheme='socks5h', https_host='acme.org', https_port=80)
assert s.proxies == {'http': 'socks5://acme.com:999', 'https': 'socks5h://acme.org:80'}
# one can not specify a scheme or a port without passing a host
with pytest.raises(ValueError):
s.set_http_proxy(http_scheme='socks5')
with pytest.raises(ValueError):
s.set_http_proxy(https_scheme='socks5')
with pytest.raises(ValueError):
s.set_http_proxy(http_port=999)
with pytest.raises(ValueError):
s.set_http_proxy(https_port=999)
# use_http_proxy_for_https_requests requires http related information to be present
with pytest.raises(ValueError):
s.set_http_proxy(use_http_proxy_for_https_requests=True)
# reuse http info for https
s.set_http_proxy(http_url='http://10.10.10.10:80', use_http_proxy_for_https_requests=True)
assert s.proxies == {'http': 'http://10.10.10.10:80', 'https': 'http://10.10.10.10:80'}
# replace instead of update
s.set_http_proxy(https_url='http://10.10.10.20:80', replace=True)
assert s.proxies == {'https': 'http://10.10.10.20:80'}
# https url
s.set_http_proxy(https_url='http://10.10.10.20:80', replace=True)
@pytest.mark.parametrize('scheme', ('http://', 'HTTP://', 'hTTp://', 'HttP://'))
def test_mixed_case_scheme_acceptable(self, httpbin, scheme):
s = requests.Session()