def test_host_fallback(self): ably = AblyRest(token="foo") self.assertIn('http_max_retry_count', ably.http.CONNECTION_RETRY_DEFAULTS) def make_url(host): base_url = "%s://%s:%d" % (ably.http.preferred_scheme, host, ably.http.preferred_port) return urljoin(base_url, '/') with mock.patch('requests.Request', wraps=requests.Request) as request_mock: with mock.patch('requests.sessions.Session.send', side_effect=requests.exceptions.RequestException) as send_mock: with self.assertRaises(requests.exceptions.RequestException): ably.http.make_request('GET', '/', skip_auth=True) self.assertEqual( send_mock.call_count, ably.http.CONNECTION_RETRY_DEFAULTS['http_max_retry_count']) expected_urls_set = set([ make_url(host) for host in ([ably.http.preferred_host] + Defaults.get_fallback_rest_hosts(Options())) ]) for ((__, url), ___) in request_mock.call_args_list: self.assertIn(url, expected_urls_set) expected_urls_set.remove(url)
def test_host_fallback(self): ably = AblyRest(token="foo") self.assertIn('http_max_retry_count', ably.http.CONNECTION_RETRY_DEFAULTS) def make_url(host): base_url = "%s://%s:%d" % (ably.http.preferred_scheme, host, ably.http.preferred_port) return urljoin(base_url, '/') with mock.patch('requests.Request', wraps=requests.Request) as request_mock: with mock.patch('requests.sessions.Session.send', side_effect=requests.exceptions.RequestException ) as send_mock: with self.assertRaises(requests.exceptions.RequestException): ably.http.make_request('GET', '/', skip_auth=True) self.assertEqual( send_mock.call_count, ably.http. CONNECTION_RETRY_DEFAULTS['http_max_retry_count']) expected_urls_set = set([ make_url(host) for host in ([ably.http.preferred_host] + Defaults.get_fallback_rest_hosts(Options())) ]) for ((__, url), ___) in request_mock.call_args_list: self.assertIn(url, expected_urls_set) expected_urls_set.remove(url)
def make_request(self, method, path, headers=None, body=None, native_data=None, skip_auth=False, timeout=None): fallback_hosts = Defaults.get_fallback_rest_hosts(self.__options) if fallback_hosts: fallback_hosts.insert(0, self.preferred_host) fallback_hosts = itertools.cycle(fallback_hosts) if native_data is not None and body is not None: raise ValueError("make_request takes either body or native_data") elif native_data is not None: body = self.dump_body(native_data) if body: all_headers = HttpUtils.default_post_headers( self.options.use_binary_protocol) else: all_headers = HttpUtils.default_get_headers( self.options.use_binary_protocol) if not skip_auth: if self.auth.auth_mechanism == Auth.Method.BASIC and self.preferred_scheme.lower() == 'http': raise AblyException( "Cannot use Basic Auth over non-TLS connections", 401, 40103) all_headers.update(self.auth._get_auth_headers()) if headers: all_headers.update(headers) http_open_timeout = self.http_open_timeout http_request_timeout = self.http_request_timeout if fallback_hosts: http_max_retry_count = self.http_max_retry_count else: http_max_retry_count = 1 http_max_retry_duration = self.http_max_retry_duration requested_at = time.time() for retry_count in range(http_max_retry_count): host = next(fallback_hosts) if fallback_hosts else self.preferred_host if self.options.environment: host = self.options.environment + '-' + host base_url = "%s://%s:%d" % (self.preferred_scheme, host, self.preferred_port) url = urljoin(base_url, path) request = requests.Request(method, url, data=body, headers=all_headers) prepped = self.__session.prepare_request(request) try: response = self.__session.send( prepped, timeout=(http_open_timeout, http_request_timeout)) except Exception as e: # Need to catch `Exception`, see: # https://github.com/kennethreitz/requests/issues/1236#issuecomment-133312626 # if last try or cumulative timeout is done, throw exception up time_passed = time.time() - requested_at if retry_count == http_max_retry_count - 1 or \ time_passed > http_max_retry_duration: raise e else: try: AblyException.raise_for_response(response) return Response(response) except AblyException as e: if not e.is_server_error: raise e