async def test_backoff_calculation():
    client = Client(user_config=CLIENT_CONFIG)

    response_429 = (await mocks.mock_GET_HTTP_Client_response_429())[1]
    # ^ has a 1 second difference in retry and datetime
    # backoff should be 2 by Okta standards

    retry_limit_reset = float(response_429.headers["X-Rate-Limit-Reset"])
    date_time = convert_date_time_to_seconds(response_429.headers["Date"])

    backoff_time = client.get_request_executor().calculate_backoff(
        retry_limit_reset, date_time)

    assert (backoff_time == 2)
示例#2
0
    async def fire_request_helper(self, request, attempts, request_start_time):
        """
        Helper method to perform HTTP call with retries if needed

        Args:
            request (dict): HTTP request representation
            attempts (int): number of attempted HTTP calls so far
            request_start_time (float): original start time of request

        Returns:
            aiohttp.RequestInfo, aiohttp.ClientResponse, json, Exception: Tuple
            of request, response object, response json, and error if raised
        """
        # Get start request time
        current_req_start_time = time.time()
        max_retries = self._max_retries
        req_timeout = self._request_timeout

        if req_timeout > 0 and \
                (current_req_start_time - request_start_time) > req_timeout:
            # Timeout is hit for request
            return (None, None, None, Exception("Request Timeout exceeded."))

        # Execute request
        _, res_details, resp_body, error = \
            await self._http_client.send_request(request)
        # return immediately if request failed to launch (e.g. network is down, thus res_details is None)
        if res_details is None:
            return (None, None, None, error)

        headers = res_details.headers

        if attempts < max_retries and self.is_retryable_status(res_details.status):
            date_time = headers.get("Date", "")
            if date_time:
                date_time = convert_date_time_to_seconds(date_time)
            retry_limit_reset_headers = list(map(float, headers.getall(
                "X-Rate-Limit-Reset", [])))
            # header might be in lowercase, so check this too
            retry_limit_reset_headers.extend(list(map(float, headers.getall(
                "x-rate-limit-reset", []))))
            retry_limit_reset = min(retry_limit_reset_headers) if len(
                retry_limit_reset_headers) > 0 else None
            if not date_time or not retry_limit_reset:
                return (None, res_details, resp_body,
                        Exception(
                            ERROR_MESSAGE_429_MISSING_DATE_X_RESET
                        ))

            check_429 = self.is_too_many_requests(res_details.status, resp_body)
            if check_429:
                # backoff
                backoff_seconds = self.calculate_backoff(
                    retry_limit_reset, date_time)
                logger.info(f'Hit rate limit. Retry request in {backoff_seconds} seconds.')
                logger.debug(f'Value of retry_limit_reset: {retry_limit_reset}')
                logger.debug(f'Value of date_time: {date_time}')
                self.pause_for_backoff(backoff_seconds)
                if (current_req_start_time + backoff_seconds)\
                        - request_start_time > req_timeout and req_timeout > 0:
                    return (None, res_details, resp_body, resp_body)

            # Setup retry request
            attempts += 1
            request['headers'].update(
                {
                    RequestExecutor.RETRY_FOR_HEADER: headers.get(
                        "X-Okta-Request-Id", ""),
                    RequestExecutor.RETRY_COUNT_HEADER: str(attempts)
                }
            )

            _, res_details, resp_body, error = await self.fire_request_helper(
                request, attempts, request_start_time
            )
            if error:
                return (None, res_details, resp_body, error)

        return (request, res_details, resp_body, error)
示例#3
0
    async def fire_request_helper(self, request, attempts, request_start_time):
        """
        Helper method to perform HTTP call with retries if needed

        Args:
            request (dict): HTTP request representation
            attempts (int): number of attempted HTTP calls so far
            request_start_time (float): original start time of request

        Returns:
            aiohttp.RequestInfo, aiohttp.ClientResponse, json, Exception: Tuple
            of request, response object, response json, and error if raised
        """
        # Get start request time
        current_req_start_time = time.time()
        max_retries = self._max_retries
        req_timeout = self._request_timeout

        if req_timeout > 0 and \
                (current_req_start_time - request_start_time) > req_timeout:
            # Timeout is hit for request
            return (None, None, None, Exception("Request Timeout exceeded."))

        # Execute request
        _, res_details, resp_body, error = \
            await self._http_client.send_request(request)

        check_429 = self.is_too_many_requests(res_details.status, resp_body)
        headers = res_details.headers

        if attempts < max_retries and (error or check_429):
            date_time = headers.get("Date", "")
            if date_time:
                date_time = convert_date_time_to_seconds(date_time)
            retry_limit_reset_headers = list(
                map(float, headers.getall("X-Rate-Limit-Reset", [])))
            retry_limit_reset = min(retry_limit_reset_headers) if len(
                retry_limit_reset_headers) > 0 else None
            if not date_time or not retry_limit_reset:
                return (None, res_details, resp_body,
                        Exception(ERROR_MESSAGE_429_MISSING_DATE_X_RESET))

            if check_429:
                # backoff
                backoff_seconds = self.calculate_backoff(
                    retry_limit_reset, date_time)
                self.pause_for_backoff(backoff_seconds)
                if (current_req_start_time + backoff_seconds)\
                        - request_start_time > req_timeout and req_timeout > 0:
                    return (None, res_details, resp_body, resp_body)

            # Setup retry request
            attempts += 1
            request['headers'].update({
                RequestExecutor.RETRY_FOR_HEADER:
                headers.get("X-Okta-Request-Id", ""),
                RequestExecutor.RETRY_COUNT_HEADER:
                attempts
            })

            _, res_details, resp_body, error = await self.fire_request_helper(
                request, attempts, request_start_time)
            if error:
                return (None, res_details, resp_body, error)

        return (request, res_details, resp_body, error)