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_no_retry_if_not_500_to_599_http_code(self):
        default_host = Defaults.get_rest_host(Options())
        ably = AblyRest(token="foo")
        self.assertIn('http_max_retry_count', ably.http.CONNECTION_RETRY_DEFAULTS)

        default_url = "%s://%s:%d/" % (
            ably.http.preferred_scheme,
            default_host,
            ably.http.preferred_port)

        def raise_ably_exception(*args, **kwagrs):
            raise AblyException(message="",
                                status_code=600,
                                code=50500)

        with mock.patch('requests.Request', wraps=requests.Request) as request_mock:
            with mock.patch('ably.util.exceptions.AblyException.raise_for_response',
                            side_effect=raise_ably_exception) as send_mock:
                with self.assertRaises(AblyException):
                    ably.http.make_request('GET', '/', skip_auth=True)

                self.assertEqual(send_mock.call_count, 1)
                self.assertEqual(
                    request_mock.call_args,
                    mock.call(mock.ANY, default_url, data=mock.ANY, headers=mock.ANY))
 def test_tls_can_be_disabled(self):
     ably = AblyRest(token='foo', tls=False)
     self.assertFalse(ably.options.tls,
                      msg="Expected encryption to be False")
     self.assertEqual(Defaults.port,
                      Defaults.get_port(ably.options),
                      msg="Unexpected port mismatch")
    def test_no_retry_if_not_500_to_599_http_code(self):
        default_host = Defaults.get_rest_host(Options())
        ably = AblyRest(token="foo")
        self.assertIn('http_max_retry_count',
                      ably.http.CONNECTION_RETRY_DEFAULTS)

        default_url = "%s://%s:%d/" % (ably.http.preferred_scheme,
                                       default_host, ably.http.preferred_port)

        def raise_ably_exception(*args, **kwagrs):
            raise AblyException(message="", status_code=600, code=50500)

        with mock.patch('requests.Request',
                        wraps=requests.Request) as request_mock:
            with mock.patch(
                    'ably.util.exceptions.AblyException.raise_for_response',
                    side_effect=raise_ably_exception) as send_mock:
                with self.assertRaises(AblyException):
                    ably.http.make_request('GET', '/', skip_auth=True)

                self.assertEqual(send_mock.call_count, 1)
                self.assertEqual(
                    request_mock.call_args,
                    mock.call(mock.ANY,
                              default_url,
                              data=mock.ANY,
                              headers=mock.ANY))
 def test_tls_defaults_to_true(self):
     ably = AblyRest(token='foo')
     self.assertTrue(ably.options.tls,
                     msg="Expected encryption to default to true")
     self.assertEqual(Defaults.tls_port,
                      Defaults.get_port(ably.options),
                      msg="Unexpected port mismatch")
    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_specified_tls_port(self):
     ably = AblyRest(token='foo', tls_port=9999, tls=True)
     self.assertEqual(
         9999,
         Defaults.get_port(ably.options),
         msg="Unexpected port mismatch. Expected: 9999. Actual: %d" %
         ably.options.tls_port)
Example #8
0
    def __get_rest_hosts(self):
        """
        Return the list of hosts as they should be tried. First comes the main
        host. Then the fallback hosts in random order.
        The returned list will have a length of up to http_max_retry_count.
        """
        # Defaults
        host = self.rest_host
        if host is None:
            host = Defaults.rest_host

        environment = self.environment
        if environment is None:
            environment = Defaults.environment

        http_max_retry_count = self.http_max_retry_count
        if http_max_retry_count is None:
            http_max_retry_count = Defaults.http_max_retry_count

        # Prepend environment
        if environment != 'production':
            host = '%s-%s' % (environment, host)

        # Fallback hosts
        fallback_hosts = self.fallback_hosts
        if fallback_hosts is None:
            if host == Defaults.rest_host or self.fallback_hosts_use_default:
                fallback_hosts = Defaults.fallback_hosts
            elif environment != 'production':
                fallback_hosts = Defaults.get_environment_fallback_hosts(environment)
            else:
                fallback_hosts = []

        # Explicit warning about deprecating the option
        if self.fallback_hosts_use_default:
            if environment != Defaults.environment:
                warnings.warn(
                    "It is no longer required to set 'fallback_hosts_use_default', the correct fallback hosts "
                    "are now inferred from the environment, 'fallback_hosts': {}"
                    .format(','.join(fallback_hosts)), DeprecationWarning
                )
            else:
                warnings.warn(
                    "It is no longer required to set 'fallback_hosts_use_default': 'fallback_hosts': {}"
                    .format(','.join(fallback_hosts)), DeprecationWarning
                )

        # Shuffle
        fallback_hosts = list(fallback_hosts)
        random.shuffle(fallback_hosts)

        # First main host
        hosts = [host] + fallback_hosts
        hosts = hosts[:http_max_retry_count]
        return hosts
Example #9
0
    def test_fallback_hosts(self):
        # Specify the fallback_hosts (RSC15a)
        fallback_hosts = [
            ['fallback1.com', 'fallback2.com'],
            [],
        ]

        # Fallback hosts specified (RSC15g1)
        for aux in fallback_hosts:
            ably = AblyRest(token='foo', fallback_hosts=aux)
            assert sorted(aux) == sorted(
                ably.options.get_fallback_rest_hosts())

        # Specify environment (RSC15g2)
        ably = AblyRest(token='foo',
                        environment='sandbox',
                        http_max_retry_count=10)
        assert sorted(
            Defaults.get_environment_fallback_hosts('sandbox')) == sorted(
                ably.options.get_fallback_rest_hosts())

        # Fallback hosts and environment not specified (RSC15g3)
        ably = AblyRest(token='foo', http_max_retry_count=10)
        assert sorted(Defaults.fallback_hosts) == sorted(
            ably.options.get_fallback_rest_hosts())

        # Specify environment and fallback_hosts_use_default, no fallback hosts (RSC15g4)
        # We specify http_max_retry_count=10 so all the fallback hosts get in the list
        ably = AblyRest(token='foo',
                        environment='not_considered',
                        fallback_hosts_use_default=True,
                        http_max_retry_count=10)
        assert sorted(Defaults.fallback_hosts) == sorted(
            ably.options.get_fallback_rest_hosts())

        # RSC15f
        ably = AblyRest(token='foo')
        assert 600000 == ably.options.fallback_retry_timeout
        ably = AblyRest(token='foo', fallback_retry_timeout=1000)
        assert 1000 == ably.options.fallback_retry_timeout

        with warnings.catch_warnings(record=True) as ws:
            # Cause all warnings to always be triggered
            warnings.simplefilter("always")
            AblyRest(token='foo', fallback_hosts_use_default=True)
            # Verify warning is raised for fallback_hosts_use_default
            ws = [w for w in ws if issubclass(w.category, DeprecationWarning)]
            assert len(ws) == 1
Example #10
0
    def wrapper(http, *args, **kwargs):
        try:
            return func(http, *args, **kwargs)
        except requests.exceptions.ConnectionError as e:
            # if we cannot attempt a fallback, re-raise
            # TODO: See if we can determine why this failed
            fallback_hosts = Defaults.get_fallback_hosts(http.options)
            if kwargs.get("host") or not fallback_hosts:
                raise

        last_exception = None
        for host in fallback_hosts:
            try:
                kwargs["host"] = host
                return func(rest, *args, **kwargs)
            except requests.exceptions.ConnectionError as e:
                # TODO: as above
                last_exception = e

        raise last_exception
Example #11
0
 def preferred_host(self):
     return Defaults.get_host(self.options)
 def test_specified_non_tls_port(self):
     ably = AblyRest(token='foo', port=9998, tls=False)
     self.assertEqual(9998, Defaults.get_port(ably.options),
                      msg="Unexpected port mismatch. Expected: 9999. Actual: %d" %
                      ably.options.tls_port)
Example #13
0
 def preferred_scheme(self):
     return Defaults.get_scheme(self.options)
Example #14
0
 def test_tls_defaults_to_true(self):
     ably = AblyRest(token='foo')
     assert ably.options.tls, "Expected encryption to default to true"
     assert Defaults.tls_port == Defaults.get_port(
         ably.options), "Unexpected port mismatch"
Example #15
0
 def preferred_host(self):
     return Defaults.get_rest_host(self.options)
Example #16
0
 def preferred_port(self):
     return Defaults.get_port(self.options)
Example #17
0
 def test_tls_can_be_disabled(self):
     ably = AblyRest(Options(tls=False))
     self.assertFalse(ably.options.tls,
             msg="Expected encryption to be False")
     self.assertEqual(Defaults.port, Defaults.get_port(ably.options),
             msg="Unexpected port mismatch")
Example #18
0
    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
Example #19
0
 def test_specified_non_tls_port(self):
     ably = AblyRest(token='foo', port=9998, tls=False)
     assert 9998 == Defaults.get_port(
         ably.options
     ), "Unexpected port mismatch. Expected: 9999. Actual: %d" % ably.options.tls_port
Example #20
0
 def test_tls_defaults_to_true(self):
     ably = AblyRest()
     self.assertTrue(ably.options.tls,
             msg="Expected encryption to default to true")
     self.assertEqual(Defaults.tls_port, Defaults.get_port(ably.options),
             msg="Unexpected port mismatch")
Example #21
0
 def preferred_port(self):
     return Defaults.get_port(self.options)
Example #22
0
 def test_tls_can_be_disabled(self):
     ably = AblyRest(token='foo', tls=False)
     assert not ably.options.tls, "Expected encryption to be False"
     assert Defaults.port == Defaults.get_port(
         ably.options), "Unexpected port mismatch"
Example #23
0
 def test_specified_tls_port(self):
     ably = AblyRest(token='foo', tls_port=9999, tls=True)
     assert 9999 == Defaults.get_port(ably.options), "Unexpected port mismatch. Expected: 9999. Actual: %d" % ably.options.tls_port
Example #24
0
 def test_tls_defaults_to_true(self):
     ably = AblyRest(token='foo')
     assert ably.options.tls, "Expected encryption to default to true"
     assert Defaults.tls_port == Defaults.get_port(ably.options), "Unexpected port mismatch"
Example #25
0
 def test_tls_can_be_disabled(self):
     ably = AblyRest(token='foo', tls=False)
     assert not ably.options.tls, "Expected encryption to be False"
     assert Defaults.port == Defaults.get_port(ably.options), "Unexpected port mismatch"
Example #26
0
 def preferred_scheme(self):
     return Defaults.get_scheme(self.options)
Example #27
0
 def test_specified_port(self):
     ably = AblyRest(Options(port=9998, tls_port=9999))
     self.assertEqual(9999, Defaults.get_port(ably.options),
             msg="Unexpected port mismatch. Expected: 9999. Actual: %d" % ably.options.tls_port)