Skip to content

Commit 5f9fef3

Browse files
committed
Keep auth session alive when inactive
1 parent f62b6ee commit 5f9fef3

3 files changed

Lines changed: 65 additions & 4 deletions

File tree

yamcs-client/src/yamcs/client/core.py

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -155,6 +155,7 @@ def __init__(
155155
tls_verify: Union[bool, str] = True,
156156
credentials: Optional[Credentials] = None,
157157
user_agent: Optional[str] = None,
158+
keep_alive: bool = True,
158159
on_token_update: Optional[Callable[[Credentials], None]] = None,
159160
):
160161
"""
@@ -172,6 +173,13 @@ def __init__(
172173
Credentials for when the server is secured
173174
:param user_agent:
174175
Optionally override the default user agent
176+
:param keep_alive:
177+
Automatically renew the client session. If disabled,
178+
the session will terminate after about 30 minutes of
179+
inactivity.
180+
181+
This property is only considered when accessing a
182+
server that requires authentication.
175183
"""
176184

177185
# Allow server URLs.
@@ -190,6 +198,7 @@ def __init__(
190198
user_agent=user_agent,
191199
on_token_update=on_token_update,
192200
tls_verify=tls_verify,
201+
keep_alive=keep_alive,
193202
)
194203

195204
@staticmethod

yamcs-client/src/yamcs/core/context.py

Lines changed: 23 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
from datetime import datetime, timezone
12
from typing import Callable, Optional, Union
23

34
import requests
@@ -7,11 +8,11 @@
78
from yamcs.api import exception_pb2
89
from yamcs.core.auth import Credentials
910
from yamcs.core.exceptions import NotFound, Unauthorized, YamcsError
10-
from yamcs.core.helpers import do_request
11+
from yamcs.core.helpers import FixedDelay, do_request
1112

1213

1314
class Context:
14-
credentials = None
15+
credentials: Optional[Credentials] = None
1516

1617
def __init__(
1718
self,
@@ -21,6 +22,7 @@ def __init__(
2122
user_agent: Optional[str] = None,
2223
on_token_update: Optional[Callable[[Credentials], None]] = None,
2324
tls_verify: Union[bool, str] = True,
25+
keep_alive: bool = True,
2426
):
2527
if address.endswith("/"):
2628
self.address = address[:-1]
@@ -51,9 +53,23 @@ def __init__(
5153
urllib3.disable_warnings(urllib3.exceptions.InsecureRequestWarning)
5254

5355
if credentials:
54-
self.credentials = credentials.login(
56+
converted_creds = credentials.login(
5557
self.session, self.auth_root, on_token_update
5658
)
59+
self.credentials = converted_creds
60+
61+
# An assigned refresh token lives only for about 30 minutes. We actively
62+
# extend it, so that the session survives when idle.
63+
if converted_creds.expiry and keep_alive:
64+
65+
def renew_session():
66+
expiry = converted_creds.expiry
67+
if expiry:
68+
remaining = expiry - datetime.now(tz=timezone.utc)
69+
if 0 < remaining.total_seconds() < 60:
70+
converted_creds.refresh(self.session, self.auth_root)
71+
72+
self._session_renewer = FixedDelay(renew_session, 10)
5773

5874
if not user_agent:
5975
user_agent = "python-yamcs-client v" + clientversion.__version__
@@ -121,4 +137,8 @@ def request(self, method: str, path: str, **kwargs) -> requests.Response:
121137

122138
def close(self):
123139
"""Close this context"""
140+
141+
if self._session_renewer:
142+
self._session_renewer.stop()
143+
124144
self.session.close()

yamcs-client/src/yamcs/core/helpers.py

Lines changed: 33 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,8 @@
44
import logging
55
import os
66
from datetime import datetime, timezone
7-
from typing import Any, Iterator, List, Union
7+
from threading import Timer
8+
from typing import Any, Callable, Iterator, List, Union
89
from urllib.parse import urlparse
910

1011
import requests
@@ -359,3 +360,34 @@ def clear(self):
359360
copy = list(repeatable)
360361
for item in copy:
361362
repeatable.remove(item)
363+
364+
365+
class FixedDelay:
366+
"""
367+
Helper class to run a periodic action, with a fixed delay between
368+
the termination of one execution, and the commencement of the next.
369+
"""
370+
371+
def __init__(self, action: Callable[[], None], period: float):
372+
self._timer = None
373+
self.action = action
374+
self.period = period
375+
self.is_running = False
376+
self.start()
377+
378+
def start(self):
379+
if not self.is_running:
380+
self._timer = Timer(self.period, self._run)
381+
self._timer.daemon = True
382+
self._timer.start()
383+
self.is_running = True
384+
385+
def stop(self):
386+
if self._timer:
387+
self._timer.cancel()
388+
self.is_running = False
389+
390+
def _run(self):
391+
self.is_running = False
392+
self.start()
393+
self.action()

0 commit comments

Comments
 (0)