Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix: Refresh auth token on 500 #338

Merged
merged 2 commits into from
Jan 19, 2024
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 5 additions & 2 deletions src/firebolt/client/auth/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
from httpx import Request, Response, codes

from firebolt.utils.token_storage import TokenSecureStorage
from firebolt.utils.util import Timer, cached_property
from firebolt.utils.util import Timer, cached_property, get_internal_error_code


class AuthRequest(Request):
Expand Down Expand Up @@ -128,7 +128,10 @@ def auth_flow(self, request: Request) -> Generator[Request, Response, None]:

response = yield request

if response.status_code == codes.UNAUTHORIZED:
if (
response.status_code == codes.UNAUTHORIZED
or get_internal_error_code(response) == codes.UNAUTHORIZED
):
yield from self.get_new_token_generator()
request.headers["Authorization"] = f"Bearer {self.token}"
yield request
Expand Down
32 changes: 31 additions & 1 deletion src/firebolt/utils/util.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
from types import TracebackType
from typing import TYPE_CHECKING, Callable, Optional, Type, TypeVar

from httpx import URL
from httpx import URL, Response, codes

from firebolt.utils.exception import ConfigurationError

Expand Down Expand Up @@ -90,6 +90,36 @@ def get_auth_endpoint(api_endpoint: URL) -> URL:
)


def get_internal_error_code(response: Response) -> Optional[int]:
"""Get internal error code from response.

Args:
response (Response): HTTP response

Returns:
Optional[int]: Internal error code
"""
# Internal server error usually hides the real error code in the response body
if response.status_code == codes.INTERNAL_SERVER_ERROR:
try:
# Example response:
# Received error from remote server
# /core/v1/accounts/<account_num>/engines:getIdByName?engine_name=
# <engine_name>. HTTP status code: 401 Unauthorized, body: failed to
# verify JWT token: failed to verify jwt: "exp" not satisfied\n'
error = int(response.text.split("HTTP status code: ")[1].split(" ")[0])
body = (
response.text.split("body: ")[1] if "body: " in response.text else None
)
logger.debug(
f"Detected an internal server error with code: {error}, body: {body}"
)
return error
except (IndexError, ValueError):
return None
return None


def merge_urls(base: URL, merge: URL) -> URL:
"""Merge a base and merge urls.

Expand Down
27 changes: 27 additions & 0 deletions tests/unit/utils/test_utils.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
import pytest
from httpx import Response

from firebolt.utils.util import get_internal_error_code


@pytest.mark.parametrize(
"status_code, content, expected_error_code",
[
(200, b"No error code here", None),
(500, b"No error code here", None),
(
500,
b"HTTP status code: 401 Unauthorized, body: failed to verify JWT token",
401,
),
(500, b"HTTP status code: 401 Unauthorized", 401),
(
500,
b"HTTP status code: Unauthorized, body: failed to verify JWT token",
None,
),
],
)
def test_get_internal_error_code(status_code, content, expected_error_code):
response = Response(status_code=status_code, content=content)
assert get_internal_error_code(response) == expected_error_code
Loading