Skip to content

Commit 4b527f8

Browse files
committed
fix retry limit token
1 parent f1b37a5 commit 4b527f8

2 files changed

Lines changed: 62 additions & 20 deletions

File tree

sinch/core/adapters/requests_http_transport.py

Lines changed: 5 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ def __init__(self, sinch):
99
super().__init__(sinch)
1010
self.http_session = requests.Session()
1111

12-
def request(self, endpoint: HTTPEndpoint) -> HTTPResponse:
12+
def send(self, endpoint: HTTPEndpoint) -> HTTPResponse:
1313
request_data: HttpRequest = self.prepare_request(endpoint)
1414
request_data: HttpRequest = self.authenticate(endpoint, request_data)
1515

@@ -34,11 +34,8 @@ def request(self, endpoint: HTTPEndpoint) -> HTTPResponse:
3434
f"and body: {response_body} from URL: {request_data.url}"
3535
)
3636

37-
return self.handle_response(
38-
endpoint=endpoint,
39-
http_response=HTTPResponse(
40-
status_code=response.status_code,
41-
body=response_body,
42-
headers=response.headers
43-
)
37+
return HTTPResponse(
38+
status_code=response.status_code,
39+
body=response_body,
40+
headers=response.headers
4441
)

sinch/core/ports/http_transport.py

Lines changed: 57 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -10,12 +10,59 @@
1010

1111

1212
class HTTPTransport(ABC):
13+
"""Base class for HTTP transports.
14+
15+
Subclasses implement ``send`` to perform the raw HTTP call.
16+
The public ``request`` method adds cross-cutting concerns on top:
17+
authentication, logging hooks, and automatic token refresh on 401.
18+
"""
19+
1320
def __init__(self, sinch):
1421
self.sinch = sinch
1522

23+
# ------------------------------------------------------------------
24+
# Subclass contract
25+
# ------------------------------------------------------------------
26+
1627
@abstractmethod
28+
def send(self, endpoint: HTTPEndpoint) -> HTTPResponse:
29+
"""Execute a single HTTP round-trip and return the response.
30+
31+
Implementations must prepare the request, authenticate, perform the
32+
HTTP call, deserialize the response, and return an ``HTTPResponse``.
33+
They should **not** handle token refresh — that is done by
34+
``request``.
35+
"""
36+
37+
# ------------------------------------------------------------------
38+
# Public API
39+
# ------------------------------------------------------------------
40+
1741
def request(self, endpoint: HTTPEndpoint) -> HTTPResponse:
18-
pass
42+
"""Send a request with automatic OAuth token refresh on 401.
43+
44+
If the server responds with 401 *and* the token is detected as
45+
expired, the token is invalidated and **one** retry is attempted
46+
with a fresh token. A second consecutive 401 is handed straight
47+
to the endpoint's error handler — no further retries.
48+
"""
49+
http_response = self.send(endpoint)
50+
51+
if self._should_refresh_token(endpoint, http_response):
52+
self.sinch.configuration.token_manager.handle_invalid_token(
53+
http_response
54+
)
55+
if (
56+
self.sinch.configuration.token_manager.token_state
57+
== TokenState.EXPIRED
58+
):
59+
http_response = self.send(endpoint)
60+
61+
return endpoint.handle_response(http_response)
62+
63+
# ------------------------------------------------------------------
64+
# Internals
65+
# ------------------------------------------------------------------
1966

2067
def authenticate(self, endpoint, request_data):
2168
if endpoint.HTTP_AUTHENTICATION in (HTTPAuthentication.BASIC.value, HTTPAuthentication.OAUTH.value):
@@ -83,10 +130,7 @@ def deserialize_json_response(response):
83130
response_body = response.json()
84131
except ValueError as err:
85132
raise SinchException(
86-
message=(
87-
"Error while parsing json response.",
88-
err.msg
89-
),
133+
message=f"Error while parsing json response. {err}",
90134
is_from_server=True,
91135
response=response
92136
)
@@ -95,10 +139,11 @@ def deserialize_json_response(response):
95139

96140
return response_body
97141

98-
def handle_response(self, endpoint: HTTPEndpoint, http_response: HTTPResponse):
99-
if http_response.status_code == 401 and endpoint.HTTP_AUTHENTICATION == HTTPAuthentication.OAUTH.value:
100-
self.sinch.configuration.token_manager.handle_invalid_token(http_response)
101-
if self.sinch.configuration.token_manager.token_state == TokenState.EXPIRED:
102-
return self.request(endpoint=endpoint)
103-
104-
return endpoint.handle_response(http_response)
142+
@staticmethod
143+
def _should_refresh_token(endpoint, http_response):
144+
"""Return True when a 401 response should trigger a token refresh."""
145+
return (
146+
http_response.status_code == 401
147+
and endpoint.HTTP_AUTHENTICATION
148+
== HTTPAuthentication.OAUTH.value
149+
)

0 commit comments

Comments
 (0)