Skip to content

Commit 3f8f9d7

Browse files
authored
Sem-Ver: api-break Share request sessions across key retriever instances so as to use a common cache.
This change makes it so that when instantiating the HTTPSPublicKeyRetriever class multiple times, they all share the same requests session with cache. Previously, each instance would create its own session with isolated cache. Since the library by default always creates a new instance of the ASAP_KEY_RETRIEVER_CLASS class (e.g. common.backend.Backend#get_verifier), cached responses were never reused.
1 parent eb92484 commit 3f8f9d7

4 files changed

Lines changed: 61 additions & 4 deletions

File tree

atlassian_jwt_auth/contrib/aiohttp/key.py

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111

1212
class HTTPSPublicKeyRetriever(_HTTPSPublicKeyRetriever):
1313
"""A class for retrieving JWT public keys with aiohttp"""
14+
_class_session = None
1415

1516
def __init__(self, base_url, *, loop=None):
1617
if loop is None:
@@ -19,7 +20,10 @@ def __init__(self, base_url, *, loop=None):
1920
super().__init__(base_url)
2021

2122
def _get_session(self):
22-
return aiohttp.ClientSession(loop=self.loop)
23+
if HTTPSPublicKeyRetriever._class_session is None:
24+
HTTPSPublicKeyRetriever._class_session = aiohttp.ClientSession(
25+
loop=self.loop)
26+
return HTTPSPublicKeyRetriever._class_session
2327

2428
async def _retrieve(self, url, requests_kwargs):
2529
try:

atlassian_jwt_auth/key.py

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,9 @@ class HTTPSPublicKeyRetriever(BasePublicKeyRetriever):
7272
""" This class retrieves public key from a https location based upon the
7373
given key id.
7474
"""
75+
# Use a static requests session, reused/shared by all instances of
76+
# HTTPSPublicKeyRetriever:
77+
_class_session = None
7578

7679
def __init__(self, base_url):
7780
if base_url is None or not base_url.startswith('https://'):
@@ -83,9 +86,10 @@ def __init__(self, base_url):
8386
self._session = self._get_session()
8487

8588
def _get_session(self):
86-
session = requests.Session()
87-
session.mount('https://', cachecontrol.CacheControlAdapter())
88-
return session
89+
if HTTPSPublicKeyRetriever._class_session is None:
90+
session = cachecontrol.CacheControl(requests.Session())
91+
HTTPSPublicKeyRetriever._class_session = session
92+
return HTTPSPublicKeyRetriever._class_session
8993

9094
def retrieve(self, key_identifier, **requests_kwargs):
9195
""" returns the public key for given key_identifier. """

atlassian_jwt_auth/tests/test_public_key_provider.py

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
1+
import re
12
import unittest
23

34
import mock
5+
import httptest
46
import requests
57

68
from atlassian_jwt_auth.key import (
@@ -93,6 +95,52 @@ def test_retrieve_fails_with_forbidden_error(self, mock_get_method):
9395
retriever.retrieve('example/eg')
9496

9597

98+
class CachedHTTPPublicKeyRetrieverTest(utils.ES256KeyTestMixin,
99+
unittest.TestCase):
100+
101+
class HTTPPublicKeyRetriever(HTTPSPublicKeyRetriever):
102+
"""A subclass of HTTPSPublicKeyRetriever that allows us to use plain
103+
HTTP during testing so we don't have to run an actual SSL server.
104+
"""
105+
106+
def __init__(self, base_url):
107+
# pretend to the super class that this is an HTTPS url
108+
super(CachedHTTPPublicKeyRetrieverTest.HTTPPublicKeyRetriever,
109+
self).__init__(
110+
re.sub(r'^http', 'https', base_url, flags=re.IGNORECASE))
111+
self.base_url = base_url
112+
113+
def setUp(self):
114+
super(CachedHTTPPublicKeyRetrieverTest, self).setUp()
115+
self._private_key_pem = self.get_new_private_key_in_pem_format()
116+
self._public_key_pem = utils.get_public_key_pem_for_private_key_pem(
117+
self._private_key_pem)
118+
119+
def test_http_caching(self):
120+
"""Asserts that our use of requests properly caches keys between
121+
invocations across different `HTTPSPublicKeyRetriever` instances.
122+
"""
123+
def wsgi(environ, start_response):
124+
print(environ['PATH_INFO'])
125+
start_response('200 OK', [
126+
('content-type', 'application/x-pem-file;charset=UTF-8'),
127+
('Cache-Control', 'public,max-age=300,stale-while-revalidate='
128+
'300,stale-if-error=300'),
129+
('Last-Modified', 'Sun, 18 Jan 1970 18:14:21 GMT')])
130+
return [self._public_key_pem]
131+
132+
with httptest.testserver(wsgi) as server:
133+
134+
retriever = self.HTTPPublicKeyRetriever(server.url())
135+
retriever.retrieve('example/eg')
136+
137+
retriever = self.HTTPPublicKeyRetriever(server.url())
138+
retriever.retrieve('example/eg')
139+
140+
self.assertEqual(1, len(server.log()),
141+
msg='HTTP caching should suppress second GET')
142+
143+
96144
class BaseHTTPSMultiRepositoryPublicKeyRetrieverTest(
97145
BaseHTTPSPublicKeyRetrieverTest):
98146
""" tests for the HTTPSMultiRepositoryPublicKeyRetriever class. """

test-requirements.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,3 +2,4 @@ nose
22
mock
33
flask<1.1.0
44
Django==1.11
5+
atlassian-httptest==0.8

0 commit comments

Comments
 (0)