Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Cache trusted host #7885

Merged
merged 5 commits into from
Apr 13, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions news/7847.feature
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Change default behaviour to always cache responses from trusted-host source.
40 changes: 28 additions & 12 deletions src/pip/_internal/network/session.py
Original file line number Diff line number Diff line change
Expand Up @@ -217,6 +217,14 @@ def cert_verify(self, conn, url, verify, cert):
)


class InsecureCacheControlAdapter(CacheControlAdapter):

def cert_verify(self, conn, url, verify, cert):
super(InsecureCacheControlAdapter, self).cert_verify(
conn=conn, url=url, verify=False, cert=cert
)


class PipSession(requests.Session):

timeout = None # type: Optional[int]
Expand Down Expand Up @@ -263,25 +271,30 @@ def __init__(self, *args, **kwargs):
backoff_factor=0.25,
)

# We want to _only_ cache responses on securely fetched origins. We do
# this because we can't validate the response of an insecurely fetched
# Our Insecure HTTPAdapter disables HTTPS validation. It does not
# support caching so we'll use it for all http:// URLs.
# If caching is disabled, we will also use it for
# https:// hosts that we've marked as ignoring
# TLS errors for (trusted-hosts).
insecure_adapter = InsecureHTTPAdapter(max_retries=retries)

# We want to _only_ cache responses on securely fetched origins or when
# the host is specified as trusted. We do this because
# we can't validate the response of an insecurely/untrusted fetched
# origin, and we don't want someone to be able to poison the cache and
# require manual eviction from the cache to fix it.
if cache:
secure_adapter = CacheControlAdapter(
cache=SafeFileCache(cache),
max_retries=retries,
)
self._trusted_host_adapter = InsecureCacheControlAdapter(
cache=SafeFileCache(cache),
max_retries=retries,
)
else:
secure_adapter = HTTPAdapter(max_retries=retries)

# Our Insecure HTTPAdapter disables HTTPS validation. It does not
# support caching (see above) so we'll use it for all http:// URLs as
# well as any https:// host that we've marked as ignoring TLS errors
# for.
insecure_adapter = InsecureHTTPAdapter(max_retries=retries)
# Save this for later use in add_insecure_host().
self._insecure_adapter = insecure_adapter
self._trusted_host_adapter = insecure_adapter

self.mount("https://", secure_adapter)
self.mount("http://", insecure_adapter)
Expand Down Expand Up @@ -310,12 +323,15 @@ def add_trusted_host(self, host, source=None, suppress_logging=False):
if host_port not in self.pip_trusted_origins:
self.pip_trusted_origins.append(host_port)

self.mount(build_url_from_netloc(host) + '/', self._insecure_adapter)
self.mount(
build_url_from_netloc(host) + '/',
self._trusted_host_adapter
)
if not host_port[1]:
# Mount wildcard ports for the same host.
self.mount(
build_url_from_netloc(host) + ':',
self._insecure_adapter
self._trusted_host_adapter
)

def iter_secure_origins(self):
Expand Down
18 changes: 9 additions & 9 deletions tests/unit/test_network_session.py
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,7 @@ def test_http_cache_is_not_enabled(self, tmpdir):

assert not hasattr(session.adapters["http://"], "cache")

def test_insecure_host_adapter(self, tmpdir):
def test_trusted_hosts_adapter(self, tmpdir):
session = PipSession(
cache=tmpdir.joinpath("test-cache"),
trusted_hosts=["example.com"],
Expand All @@ -81,14 +81,14 @@ def test_insecure_host_adapter(self, tmpdir):
assert "https://example.com/" in session.adapters
# Check that the "port wildcard" is present.
assert "https://example.com:" in session.adapters
# Check that the cache isn't enabled.
assert not hasattr(session.adapters["https://example.com/"], "cache")
# Check that the cache is enabled.
assert hasattr(session.adapters["https://example.com/"], "cache")

def test_add_trusted_host(self):
# Leave a gap to test how the ordering is affected.
trusted_hosts = ['host1', 'host3']
session = PipSession(trusted_hosts=trusted_hosts)
insecure_adapter = session._insecure_adapter
trusted_host_adapter = session._trusted_host_adapter
prefix2 = 'https://host2/'
prefix3 = 'https://host3/'
prefix3_wildcard = 'https://host3:'
Expand All @@ -97,8 +97,8 @@ def test_add_trusted_host(self):
assert session.pip_trusted_origins == [
('host1', None), ('host3', None)
]
assert session.adapters[prefix3] is insecure_adapter
assert session.adapters[prefix3_wildcard] is insecure_adapter
assert session.adapters[prefix3] is trusted_host_adapter
assert session.adapters[prefix3_wildcard] is trusted_host_adapter

assert prefix2 not in session.adapters

Expand All @@ -108,8 +108,8 @@ def test_add_trusted_host(self):
('host1', None), ('host3', None), ('host2', None)
]
# Check that prefix3 is still present.
assert session.adapters[prefix3] is insecure_adapter
assert session.adapters[prefix2] is insecure_adapter
assert session.adapters[prefix3] is trusted_host_adapter
assert session.adapters[prefix2] is trusted_host_adapter

# Test that adding the same host doesn't create a duplicate.
session.add_trusted_host('host3')
Expand All @@ -123,7 +123,7 @@ def test_add_trusted_host(self):
('host1', None), ('host3', None),
('host2', None), ('host4', 8080)
]
assert session.adapters[prefix4] is insecure_adapter
assert session.adapters[prefix4] is trusted_host_adapter

def test_add_trusted_host__logging(self, caplog):
"""
Expand Down
7 changes: 5 additions & 2 deletions tests/unit/test_req_file.py
Original file line number Diff line number Diff line change
Expand Up @@ -401,10 +401,13 @@ def test_set_finder_trusted_host(
)
assert list(finder.trusted_hosts) == ['host1', 'host2:8080']
session = finder._link_collector.session
assert session.adapters['https://host1/'] is session._insecure_adapter
assert (
session.adapters['https://host1/']
is session._trusted_host_adapter
)
assert (
session.adapters['https://host2:8080/']
is session._insecure_adapter
is session._trusted_host_adapter
)

# Test the log message.
Expand Down