From c1aa51fe187442c7fce1cfb8166ae6e0c4f2feb7 Mon Sep 17 00:00:00 2001 From: Bilal Al Date: Mon, 5 Aug 2024 19:29:38 -0700 Subject: [PATCH 1/2] Used four sessions per split host and reconnect when timing out --- splitio/api/client.py | 193 ++++++++++++++++++-------- tests/api/test_httpclient.py | 261 ++++++++++++++++++++++++++--------- 2 files changed, 328 insertions(+), 126 deletions(-) diff --git a/splitio/api/client.py b/splitio/api/client.py index c7a37194..5db1cadb 100644 --- a/splitio/api/client.py +++ b/splitio/api/client.py @@ -119,6 +119,23 @@ def _get_headers(self, extra_headers, sdk_key): headers.update(extra_headers) return headers + def _record_telemetry(self, status_code, elapsed): + """ + Record Telemetry info + + :param status_code: http request status code + :type status_code: int + + :param elapsed: response time elapsed. + :type status_code: int + """ + self._telemetry_runtime_producer.record_sync_latency(self._metric_name, elapsed) + if 200 <= status_code < 300: + self._telemetry_runtime_producer.record_successful_sync(self._metric_name, get_current_epoch_time_ms()) + return + + self._telemetry_runtime_producer.record_sync_error(self._metric_name, status_code) + class HttpClient(HttpClientBase): """HttpClient wrapper.""" @@ -140,7 +157,6 @@ def __init__(self, timeout=None, sdk_url=None, events_url=None, auth_url=None, t _LOGGER.debug("Initializing httpclient") self._timeout = timeout/1000 if timeout else None # Convert ms to seconds. self._urls = _construct_urls(sdk_url, events_url, auth_url, telemetry_url) - self._lock = threading.RLock() def get(self, server, path, sdk_key, query=None, extra_headers=None): # pylint: disable=too-many-arguments """ @@ -208,23 +224,6 @@ def post(self, server, path, sdk_key, body, query=None, extra_headers=None): # except Exception as exc: # pylint: disable=broad-except raise HttpClientException(_EXC_MSG.format(source='request')) from exc - def _record_telemetry(self, status_code, elapsed): - """ - Record Telemetry info - - :param status_code: http request status code - :type status_code: int - - :param elapsed: response time elapsed. - :type status_code: int - """ - self._telemetry_runtime_producer.record_sync_latency(self._metric_name, elapsed) - if 200 <= status_code < 300: - self._telemetry_runtime_producer.record_successful_sync(self._metric_name, get_current_epoch_time_ms()) - return - - self._telemetry_runtime_producer.record_sync_error(self._metric_name, status_code) - class HttpClientAsync(HttpClientBase): """HttpClientAsync wrapper.""" @@ -350,7 +349,7 @@ async def close_session(self): if not self._session.closed: await self._session.close() -class HttpClientKerberos(HttpClient): +class HttpClientKerberos(HttpClientBase): """HttpClient wrapper.""" def __init__(self, timeout=None, sdk_url=None, events_url=None, auth_url=None, telemetry_url=None, authentication_scheme=None, authentication_params=None): @@ -367,11 +366,22 @@ def __init__(self, timeout=None, sdk_url=None, events_url=None, auth_url=None, t :type auth_url: str :param telemetry_url: Optional alternative telemetry URL. :type telemetry_url: str + :param authentication_scheme: Optional authentication scheme to use. + :type authentication_scheme: splitio.client.config.AuthenticateScheme + :param authentication_params: Optional authentication username and password to use. + :type authentication_params: [str, str] """ _LOGGER.debug("Initializing httpclient for Kerberos auth") - HttpClient.__init__(self, timeout=timeout, sdk_url=sdk_url, events_url=events_url, auth_url=auth_url, telemetry_url=telemetry_url) + self._timeout = timeout/1000 if timeout else None # Convert ms to seconds. + self._urls = _construct_urls(sdk_url, events_url, auth_url, telemetry_url) self._authentication_scheme = authentication_scheme self._authentication_params = authentication_params + self._lock = threading.RLock() + self._sessions = {'sdk': requests.Session(), + 'events': requests.Session(), + 'auth': requests.Session(), + 'telemetry': requests.Session()} + self._set_authentication() def get(self, server, path, sdk_key, query=None, extra_headers=None): # pylint: disable=too-many-arguments """ @@ -392,21 +402,49 @@ def get(self, server, path, sdk_key, query=None, extra_headers=None): # pylint: """ with self._lock: start = get_current_epoch_time_ms() - with requests.Session() as session: - self._set_authentication(session) + try: + return self._do_get(server, path, sdk_key, query, extra_headers, start) + + except requests.exceptions.ProxyError as exc: + _LOGGER.debug("Proxy Exception caught, resetting the http session") + self._sessions[server].close() + self._sessions[server] = requests.Session() + self._set_authentication(server_name=server) try: - response = session.get( - _build_url(server, path, self._urls), - headers=self._get_headers(extra_headers, sdk_key), - params=query, - timeout=self._timeout - ) - self._record_telemetry(response.status_code, get_current_epoch_time_ms() - start) - return HttpResponse(response.status_code, response.text, response.headers) - - except Exception as exc: # pylint: disable=broad-except + return self._do_get(server, path, sdk_key, query, extra_headers, start) + + except Exception as exc: raise HttpClientException(_EXC_MSG.format(source='request')) from exc + except Exception as exc: # pylint: disable=broad-except + raise HttpClientException(_EXC_MSG.format(source='request')) from exc + + def _do_get(self, server, path, sdk_key, query, extra_headers, start): + """ + Issue a get request. + :param server: Whether the request is for SDK server, Events server or Auth server. + :typee server: str + :param path: path to append to the host url. + :type path: str + :param sdk_key: sdk key. + :type sdk_key: str + :param query: Query string passed as dictionary. + :type query: dict + :param extra_headers: key/value pairs of possible extra headers. + :type extra_headers: dict + + :return: Tuple of status_code & response text + :rtype: HttpResponse + """ + with self._sessions[server].get( + _build_url(server, path, self._urls), + headers=self._get_headers(extra_headers, sdk_key), + params=query, + timeout=self._timeout + ) as response: + self._record_telemetry(response.status_code, get_current_epoch_time_ms() - start) + return HttpResponse(response.status_code, response.text, response.headers) + def post(self, server, path, sdk_key, body, query=None, extra_headers=None): # pylint: disable=too-many-arguments """ Issue a POST request. @@ -429,31 +467,72 @@ def post(self, server, path, sdk_key, body, query=None, extra_headers=None): # """ with self._lock: start = get_current_epoch_time_ms() - with requests.Session() as session: - self._set_authentication(session) + try: + return self._do_post(server, path, sdk_key, query, extra_headers, body, start) + + except requests.exceptions.ProxyError as exc: + _LOGGER.debug("Proxy Exception caught, resetting the http session") + self._sessions[server].close() + self._sessions[server] = requests.Session() + self._set_authentication(server_name=server) try: - response = session.post( - _build_url(server, path, self._urls), - params=query, - headers=self._get_headers(extra_headers, sdk_key), - json=body, - timeout=self._timeout, - ) - self._record_telemetry(response.status_code, get_current_epoch_time_ms() - start) - return HttpResponse(response.status_code, response.text, response.headers) - except Exception as exc: # pylint: disable=broad-except + return self._do_post(server, path, sdk_key, query, extra_headers, body, start) + + except Exception as exc: raise HttpClientException(_EXC_MSG.format(source='request')) from exc - def _set_authentication(self, session): - if self._authentication_scheme == AuthenticateScheme.KERBEROS_SPNEGO: - _LOGGER.debug("Using Kerberos Spnego Authentication") - if self._authentication_params != [None, None]: - session.auth = HTTPKerberosAuth(principal=self._authentication_params[0], password=self._authentication_params[1], mutual_authentication=OPTIONAL) - else: - session.auth = HTTPKerberosAuth(mutual_authentication=OPTIONAL) - elif self._authentication_scheme == AuthenticateScheme.KERBEROS_PROXY: - _LOGGER.debug("Using Kerberos Proxy Authentication") - if self._authentication_params != [None, None]: - session.mount('https://', HTTPAdapterWithProxyKerberosAuth(principal=self._authentication_params[0], password=self._authentication_params[1])) - else: - session.mount('https://', HTTPAdapterWithProxyKerberosAuth()) + except Exception as exc: # pylint: disable=broad-except + raise HttpClientException(_EXC_MSG.format(source='request')) from exc + + def _do_post(self, server, path, sdk_key, query, extra_headers, body, start): + """ + Issue a POST request. + + :param server: Whether the request is for SDK server or Events server. + :typee server: str + :param path: path to append to the host url. + :type path: str + :param sdk_key: sdk key. + :type sdk_key: str + :param body: body sent in the request. + :type body: str + :param query: Query string passed as dictionary. + :type query: dict + :param extra_headers: key/value pairs of possible extra headers. + :type extra_headers: dict + + :return: Tuple of status_code & response text + :rtype: HttpResponse + """ + with self._sessions[server].post( + _build_url(server, path, self._urls), + params=query, + headers=self._get_headers(extra_headers, sdk_key), + json=body, + timeout=self._timeout, + ) as response: + self._record_telemetry(response.status_code, get_current_epoch_time_ms() - start) + return HttpResponse(response.status_code, response.text, response.headers) + + def _set_authentication(self, server_name=None): + """ + Set the authentication for all self._sessions variables based on authentication scheme. + + :param server: If set, will only add the auth for its session variable, otherwise will set all sessions. + :typee server: str + """ + for server in ['sdk', 'events', 'auth', 'telemetry']: + if server_name is not None and server_name != server: + continue + if self._authentication_scheme == AuthenticateScheme.KERBEROS_SPNEGO: + _LOGGER.debug("Using Kerberos Spnego Authentication") + if self._authentication_params != [None, None]: + self._sessions[server].auth = HTTPKerberosAuth(principal=self._authentication_params[0], password=self._authentication_params[1], mutual_authentication=OPTIONAL) + else: + self._sessions[server].auth = HTTPKerberosAuth(mutual_authentication=OPTIONAL) + elif self._authentication_scheme == AuthenticateScheme.KERBEROS_PROXY: + _LOGGER.debug("Using Kerberos Proxy Authentication") + if self._authentication_params != [None, None]: + self._sessions[server].mount('https://', HTTPAdapterWithProxyKerberosAuth(principal=self._authentication_params[0], password=self._authentication_params[1])) + else: + self._sessions[server].mount('https://', HTTPAdapterWithProxyKerberosAuth()) diff --git a/tests/api/test_httpclient.py b/tests/api/test_httpclient.py index 621e696a..147eb897 100644 --- a/tests/api/test_httpclient.py +++ b/tests/api/test_httpclient.py @@ -1,5 +1,5 @@ """HTTPClient test module.""" -from requests_kerberos import HTTPKerberosAuth, OPTIONAL +from requests_kerberos import HTTPKerberosAuth import pytest import unittest.mock as mock import requests @@ -153,108 +153,233 @@ def test_post_custom_urls(self, mocker): assert response.body == 'ok' assert get_mock.mock_calls == [call] - def test_authentication_scheme(self, mocker): + def test_telemetry(self, mocker): + telemetry_storage = InMemoryTelemetryStorage() + telemetry_producer = TelemetryStorageProducer(telemetry_storage) + telemetry_runtime_producer = telemetry_producer.get_telemetry_runtime_producer() + response_mock = mocker.Mock() response_mock.status_code = 200 + response_mock.headers = {} response_mock.text = 'ok' get_mock = mocker.Mock() get_mock.return_value = response_mock + mocker.patch('splitio.api.client.requests.post', new=get_mock) + httpclient = client.HttpClient(timeout=1500, sdk_url='https://sdk.com', events_url='https://events.com') + httpclient.set_telemetry_data("metric", telemetry_runtime_producer) + + self.metric1 = None + self.cur_time = 0 + def record_successful_sync(metric_name, cur_time): + self.metric1 = metric_name + self.cur_time = cur_time + httpclient._telemetry_runtime_producer.record_successful_sync = record_successful_sync + + self.metric2 = None + self.elapsed = 0 + def record_sync_latency(metric_name, elapsed): + self.metric2 = metric_name + self.elapsed = elapsed + httpclient._telemetry_runtime_producer.record_sync_latency = record_sync_latency + + self.metric3 = None + self.status = 0 + def record_sync_error(metric_name, elapsed): + self.metric3 = metric_name + self.status = elapsed + httpclient._telemetry_runtime_producer.record_sync_error = record_sync_error + + httpclient.post('sdk', 'test1', 'some_api_key', {'p1': 'a'}, {'param1': 123}, {'h1': 'abc'}) + assert (self.metric2 == "metric") + assert (self.metric1 == "metric") + assert (self.cur_time > self.elapsed) + + response_mock.status_code = 400 + response_mock.headers = {} + response_mock.text = 'ok' + httpclient.post('sdk', 'test1', 'some_api_key', {'p1': 'a'}, {'param1': 123}, {'h1': 'abc'}) + assert (self.metric3 == "metric") + assert (self.status == 400) + + # testing get call + mocker.patch('splitio.api.client.requests.get', new=get_mock) + self.metric1 = None + self.cur_time = 0 + self.metric2 = None + self.elapsed = 0 + response_mock.status_code = 200 + httpclient.get('sdk', 'test1', 'some_api_key', {'param1': 123}, {'h1': 'abc'}) + assert (self.metric2 == "metric") + assert (self.metric1 == "metric") + assert (self.cur_time > self.elapsed) + + self.metric3 = None + self.status = 0 + response_mock.status_code = 400 + httpclient.get('sdk', 'test1', 'some_api_key', {'param1': 123}, {'h1': 'abc'}) + assert (self.metric3 == "metric") + assert (self.status == 400) + +class HttpClientKerberosTests(object): + """Http Client test cases.""" + + def test_authentication_scheme(self, mocker): + global turl + global theaders + global tparams + global ttimeout + global tjson + + turl = None + theaders = None + tparams = None + ttimeout = None + class get_mock(object): + def __init__(self, url, headers, params, timeout): + global turl + global theaders + global tparams + global ttimeout + turl = url + theaders = headers + tparams = params + ttimeout = timeout + + def __enter__(self): + response_mock = mocker.Mock() + response_mock.status_code = 200 + response_mock.text = 'ok' + return response_mock + + def __exit__(self, exc_type, exc_val, exc_tb): + pass + mocker.patch('splitio.api.client.requests.Session.get', new=get_mock) httpclient = client.HttpClientKerberos(sdk_url='https://sdk.com', authentication_scheme=AuthenticateScheme.KERBEROS_SPNEGO, authentication_params=[None, None]) httpclient.set_telemetry_data("metric", mocker.Mock()) response = httpclient.get('sdk', '/test1', 'some_api_key', {'param1': 123}, {'h1': 'abc'}) - call = mocker.call( - 'https://sdk.com/test1', - headers={'Authorization': 'Bearer some_api_key', 'h1': 'abc', 'Content-Type': 'application/json'}, - params={'param1': 123}, - timeout=None - ) + assert turl == 'https://sdk.com/test1' + assert theaders == {'Authorization': 'Bearer some_api_key', 'h1': 'abc', 'Content-Type': 'application/json'} + assert tparams == {'param1': 123} + assert ttimeout == None assert response.status_code == 200 assert response.body == 'ok' - assert get_mock.mock_calls == [call] - get_mock.reset_mock() + turl = None + theaders = None + tparams = None + ttimeout = None httpclient = client.HttpClientKerberos(sdk_url='https://sdk.com', authentication_scheme=AuthenticateScheme.KERBEROS_SPNEGO, authentication_params=['bilal', 'split']) httpclient.set_telemetry_data("metric", mocker.Mock()) response = httpclient.get('sdk', '/test1', 'some_api_key', {'param1': 123}, {'h1': 'abc'}) - call = mocker.call( - 'https://sdk.com/test1', - headers={'Authorization': 'Bearer some_api_key', 'h1': 'abc', 'Content-Type': 'application/json'}, - params={'param1': 123}, - timeout=None - ) + assert turl == 'https://sdk.com/test1' + assert theaders == {'Authorization': 'Bearer some_api_key', 'h1': 'abc', 'Content-Type': 'application/json'} + assert tparams == {'param1': 123} + assert ttimeout == None + assert response.status_code == 200 assert response.body == 'ok' - assert get_mock.mock_calls == [call] - get_mock.reset_mock() response_mock = mocker.Mock() response_mock.status_code = 200 response_mock.headers = {} response_mock.text = 'ok' - get_mock = mocker.Mock() - get_mock.return_value = response_mock - mocker.patch('splitio.api.client.requests.Session.post', new=get_mock) - httpclient = client.HttpClientKerberos(sdk_url='https://sdk.com', events_url='https://events.com', authentication_scheme=AuthenticateScheme.KERBEROS_PROXY, authentication_params=[None, None]) + turl = None + theaders = None + tparams = None + ttimeout = None + tjson = None + class post_mock(object): + def __init__(self, url, params, headers, json, timeout): + global turl + global theaders + global tparams + global ttimeout + global tjson + turl = url + theaders = headers + tparams = params + ttimeout = timeout + tjson = json + + def __enter__(self): + response_mock = mocker.Mock() + response_mock.status_code = 200 + response_mock.text = 'ok' + return response_mock + + def __exit__(self, exc_type, exc_val, exc_tb): + pass + mocker.patch('splitio.api.client.requests.Session.post', new=post_mock) + + httpclient = client.HttpClientKerberos(timeout=1500, sdk_url='https://sdk.com', events_url='https://events.com', authentication_scheme=AuthenticateScheme.KERBEROS_PROXY, authentication_params=[None, None]) httpclient.set_telemetry_data("metric", mocker.Mock()) response = httpclient.post('events', 'test1', 'some_api_key', {'p1': 'a'}, {'param1': 123}, {'h1': 'abc'}) - call = mocker.call( - 'https://events.com/test1', - json={'p1': 'a'}, - headers={'Authorization': 'Bearer some_api_key', 'h1': 'abc', 'Content-Type': 'application/json'}, - params={'param1': 123}, - timeout=None - ) + assert turl == 'https://events.com/test1' + assert tjson == {'p1': 'a'} + assert theaders == {'Authorization': 'Bearer some_api_key', 'h1': 'abc', 'Content-Type': 'application/json'} + assert tparams == {'param1': 123} + assert ttimeout == 1.5 + assert response.status_code == 200 assert response.body == 'ok' - assert get_mock.mock_calls == [call] - get_mock.reset_mock() + turl = None + theaders = None + tparams = None + ttimeout = None mocker.patch('splitio.api.client.requests.Session.get', new=get_mock) - httpclient = client.HttpClientKerberos(sdk_url='https://sdk.com', authentication_scheme=AuthenticateScheme.KERBEROS_PROXY, authentication_params=['bilal', 'split']) + httpclient = client.HttpClientKerberos(timeout=1500, sdk_url='https://sdk.com', authentication_scheme=AuthenticateScheme.KERBEROS_PROXY, authentication_params=['bilal', 'split']) httpclient.set_telemetry_data("metric", mocker.Mock()) response = httpclient.get('sdk', '/test1', 'some_api_key', {'param1': 123}, {'h1': 'abc'}) - call = mocker.call( - 'https://sdk.com/test1', - headers={'Authorization': 'Bearer some_api_key', 'h1': 'abc', 'Content-Type': 'application/json'}, - params={'param1': 123}, - timeout=None - ) + assert turl == 'https://sdk.com/test1' + assert theaders == {'Authorization': 'Bearer some_api_key', 'h1': 'abc', 'Content-Type': 'application/json'} + assert tparams == {'param1': 123} + assert ttimeout == 1.5 + assert response.status_code == 200 assert response.body == 'ok' - assert get_mock.mock_calls == [call] - get_mock.reset_mock() # test auth settings httpclient = client.HttpClientKerberos(sdk_url='https://sdk.com', authentication_scheme=AuthenticateScheme.KERBEROS_SPNEGO, authentication_params=['bilal', 'split']) - my_session = requests.Session() - httpclient._set_authentication(my_session) - assert(my_session.auth.principal == 'bilal') - assert(my_session.auth.password == 'split') - assert(isinstance(my_session.auth, HTTPKerberosAuth)) - - httpclient = client.HttpClientKerberos(sdk_url='https://sdk.com', authentication_scheme=AuthenticateScheme.KERBEROS_SPNEGO, authentication_params=[None, None]) - my_session2 = requests.Session() - httpclient._set_authentication(my_session2) - assert(my_session2.auth.principal == None) - assert(my_session2.auth.password == None) - assert(isinstance(my_session2.auth, HTTPKerberosAuth)) - - httpclient = client.HttpClientKerberos(sdk_url='https://sdk.com', authentication_scheme=AuthenticateScheme.KERBEROS_PROXY, authentication_params=['bilal', 'split']) - my_session = requests.Session() - httpclient._set_authentication(my_session) - assert(my_session.adapters['https://']._principal == 'bilal') - assert(my_session.adapters['https://']._password == 'split') - assert(isinstance(my_session.adapters['https://'], client.HTTPAdapterWithProxyKerberosAuth)) - - httpclient = client.HttpClientKerberos(sdk_url='https://sdk.com', authentication_scheme=AuthenticateScheme.KERBEROS_PROXY, authentication_params=[None, None]) - my_session2 = requests.Session() - httpclient._set_authentication(my_session2) - assert(my_session2.adapters['https://']._principal == None) - assert(my_session2.adapters['https://']._password == None) - assert(isinstance(my_session2.adapters['https://'], client.HTTPAdapterWithProxyKerberosAuth)) + httpclient._set_authentication('sdk') + for server in ['sdk', 'events', 'auth', 'telemetry']: + assert(httpclient._sessions[server].auth.principal == 'bilal') + assert(httpclient._sessions[server].auth.password == 'split') + assert(isinstance(httpclient._sessions[server].auth, HTTPKerberosAuth)) + + httpclient._sessions['sdk'].close() + httpclient._sessions['events'].close() + httpclient._sessions['sdk'] = requests.Session() + httpclient._sessions['events'] = requests.Session() + assert(httpclient._sessions['sdk'].auth == None) + assert(httpclient._sessions['events'].auth == None) + + httpclient._set_authentication('sdk') + assert(httpclient._sessions['sdk'].auth.principal == 'bilal') + assert(httpclient._sessions['sdk'].auth.password == 'split') + assert(isinstance(httpclient._sessions['sdk'].auth, HTTPKerberosAuth)) + assert(httpclient._sessions['events'].auth == None) + + httpclient2 = client.HttpClientKerberos(sdk_url='https://sdk.com', authentication_scheme=AuthenticateScheme.KERBEROS_SPNEGO, authentication_params=[None, None]) + for server in ['sdk', 'events', 'auth', 'telemetry']: + assert(httpclient2._sessions[server].auth.principal == None) + assert(httpclient2._sessions[server].auth.password == None) + assert(isinstance(httpclient2._sessions[server].auth, HTTPKerberosAuth)) + + httpclient3 = client.HttpClientKerberos(sdk_url='https://sdk.com', authentication_scheme=AuthenticateScheme.KERBEROS_PROXY, authentication_params=['bilal', 'split']) + for server in ['sdk', 'events', 'auth', 'telemetry']: + assert(httpclient3._sessions[server].adapters['https://']._principal == 'bilal') + assert(httpclient3._sessions[server].adapters['https://']._password == 'split') + assert(isinstance(httpclient3._sessions[server].adapters['https://'], client.HTTPAdapterWithProxyKerberosAuth)) + + httpclient4 = client.HttpClientKerberos(sdk_url='https://sdk.com', authentication_scheme=AuthenticateScheme.KERBEROS_PROXY, authentication_params=[None, None]) + for server in ['sdk', 'events', 'auth', 'telemetry']: + assert(httpclient4._sessions[server].adapters['https://']._principal == None) + assert(httpclient4._sessions[server].adapters['https://']._password == None) + assert(isinstance(httpclient4._sessions[server].adapters['https://'], client.HTTPAdapterWithProxyKerberosAuth)) def test_telemetry(self, mocker): telemetry_storage = InMemoryTelemetryStorage() @@ -268,7 +393,7 @@ def test_telemetry(self, mocker): get_mock = mocker.Mock() get_mock.return_value = response_mock mocker.patch('splitio.api.client.requests.post', new=get_mock) - httpclient = client.HttpClient(sdk_url='https://sdk.com', events_url='https://events.com') + httpclient = client.HttpClient(timeout=1500, sdk_url='https://sdk.com', events_url='https://events.com') httpclient.set_telemetry_data("metric", telemetry_runtime_producer) self.metric1 = None @@ -323,7 +448,6 @@ def record_sync_error(metric_name, elapsed): assert (self.metric3 == "metric") assert (self.status == 400) - class MockResponse: def __init__(self, text, status, headers): self._text = text @@ -412,7 +536,6 @@ async def test_get_custom_urls(self, mocker): assert response.body == 'ok' assert get_mock.mock_calls == [call] - @pytest.mark.asyncio async def test_post(self, mocker): """Test HTTP POST verb requests.""" From d187e8c157402057f713a62c18501ce96a80be35 Mon Sep 17 00:00:00 2001 From: Bilal Al Date: Mon, 5 Aug 2024 20:41:54 -0700 Subject: [PATCH 2/2] added proxy exception test --- tests/api/test_httpclient.py | 56 ++++++++++++++++++++++++++++++++++++ 1 file changed, 56 insertions(+) diff --git a/tests/api/test_httpclient.py b/tests/api/test_httpclient.py index 147eb897..837997aa 100644 --- a/tests/api/test_httpclient.py +++ b/tests/api/test_httpclient.py @@ -381,6 +381,62 @@ def __exit__(self, exc_type, exc_val, exc_tb): assert(httpclient4._sessions[server].adapters['https://']._password == None) assert(isinstance(httpclient4._sessions[server].adapters['https://'], client.HTTPAdapterWithProxyKerberosAuth)) + def test_proxy_exception(self, mocker): + global count + count = 0 + class get_mock(object): + def __init__(self, url, params, headers, timeout): + pass + + def __enter__(self): + global count + count += 1 + if count == 1: + raise requests.exceptions.ProxyError() + + response_mock = mocker.Mock() + response_mock.status_code = 200 + response_mock.text = 'ok' + return response_mock + + def __exit__(self, exc_type, exc_val, exc_tb): + pass + + mocker.patch('splitio.api.client.requests.Session.get', new=get_mock) + httpclient = client.HttpClientKerberos(sdk_url='https://sdk.com', authentication_scheme=AuthenticateScheme.KERBEROS_SPNEGO, authentication_params=[None, None]) + httpclient.set_telemetry_data("metric", mocker.Mock()) + response = httpclient.get('sdk', '/test1', 'some_api_key', {'param1': 123}, {'h1': 'abc'}) + assert response.status_code == 200 + assert response.body == 'ok' + + count = 0 + class post_mock(object): + def __init__(self, url, params, headers, json, timeout): + pass + + def __enter__(self): + global count + count += 1 + if count == 1: + raise requests.exceptions.ProxyError() + + response_mock = mocker.Mock() + response_mock.status_code = 200 + response_mock.text = 'ok' + return response_mock + + def __exit__(self, exc_type, exc_val, exc_tb): + pass + mocker.patch('splitio.api.client.requests.Session.post', new=post_mock) + + httpclient = client.HttpClientKerberos(timeout=1500, sdk_url='https://sdk.com', events_url='https://events.com', authentication_scheme=AuthenticateScheme.KERBEROS_PROXY, authentication_params=[None, None]) + httpclient.set_telemetry_data("metric", mocker.Mock()) + response = httpclient.post('events', 'test1', 'some_api_key', {'p1': 'a'}, {'param1': 123}, {'h1': 'abc'}) + assert response.status_code == 200 + assert response.body == 'ok' + + + def test_telemetry(self, mocker): telemetry_storage = InMemoryTelemetryStorage() telemetry_producer = TelemetryStorageProducer(telemetry_storage)