mirror of
https://github.com/hansjone/netx.git
synced 2026-10-18 11:30:44 +08:00
Fix deadlock: renew_token must not call login while holding _token_lock
Non-reentrant Lock caused /v1/ume/token/refresh to hang when handshake failed or token empty inside renew; UI stuck on 续期中. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
parent
0afc77c06b
commit
ea7931496c
1 changed files with 34 additions and 30 deletions
|
|
@ -298,42 +298,46 @@ class UMEClient:
|
||||||
|
|
||||||
def renew_token(self) -> str:
|
def renew_token(self) -> str:
|
||||||
self._assert_ready()
|
self._assert_ready()
|
||||||
token = self._token_value.strip()
|
if not self._token_value.strip():
|
||||||
if not token:
|
|
||||||
return self.login(force=True)
|
return self.login(force=True)
|
||||||
|
|
||||||
|
do_login = False
|
||||||
|
renewed_value = ""
|
||||||
with self._token_lock:
|
with self._token_lock:
|
||||||
token = self._token_value.strip()
|
token = self._token_value.strip()
|
||||||
if not token:
|
if not token:
|
||||||
return self.login(force=True)
|
do_login = True
|
||||||
url = self._build_url(self.token_handshake_path)
|
else:
|
||||||
try:
|
url = self._build_url(self.token_handshake_path)
|
||||||
with self._client() as client:
|
|
||||||
resp = client.post(url, headers=self._headers(include_token=True))
|
|
||||||
if not resp.is_success:
|
|
||||||
# handshake may fail if token expired; fallback to full login
|
|
||||||
return self.login(force=True)
|
|
||||||
# Per UME guide, oauth_handshake may return no body; treat HTTP 2xx as success.
|
|
||||||
next_token = ""
|
|
||||||
ttl: int | None = None
|
|
||||||
try:
|
try:
|
||||||
text = (resp.text or "").strip()
|
with self._client() as client:
|
||||||
if text:
|
resp = client.post(url, headers=self._headers(include_token=True))
|
||||||
data = _coerce_dict(resp.json())
|
if not resp.is_success:
|
||||||
next_token, ttl = self._extract_token_and_ttl(data)
|
do_login = True
|
||||||
|
else:
|
||||||
|
next_token = ""
|
||||||
|
ttl: int | None = None
|
||||||
|
try:
|
||||||
|
text = (resp.text or "").strip()
|
||||||
|
if text:
|
||||||
|
data = _coerce_dict(resp.json())
|
||||||
|
next_token, ttl = self._extract_token_and_ttl(data)
|
||||||
|
except Exception:
|
||||||
|
next_token = ""
|
||||||
|
ttl = None
|
||||||
|
if next_token:
|
||||||
|
self._token_value = next_token
|
||||||
|
use_ttl = max(60, int(ttl)) if ttl is not None else self.token_ttl_s
|
||||||
|
self._token_expires_at = time() + use_ttl
|
||||||
|
self._last_token_source = "memory"
|
||||||
|
self._persist_token_to_store()
|
||||||
|
renewed_value = self._token_value
|
||||||
except Exception:
|
except Exception:
|
||||||
# ignore json parse errors, success is based on status code
|
do_login = True
|
||||||
next_token = ""
|
|
||||||
ttl = None
|
if do_login:
|
||||||
if next_token:
|
return self.login(force=True)
|
||||||
self._token_value = next_token
|
return renewed_value
|
||||||
# If handshake doesn't provide ttl, fall back to configured ttl.
|
|
||||||
use_ttl = max(60, int(ttl)) if ttl is not None else self.token_ttl_s
|
|
||||||
self._token_expires_at = time() + use_ttl
|
|
||||||
self._last_token_source = "memory"
|
|
||||||
self._persist_token_to_store()
|
|
||||||
return self._token_value
|
|
||||||
except Exception:
|
|
||||||
return self.login(force=True)
|
|
||||||
|
|
||||||
def logout_token(self) -> bool:
|
def logout_token(self) -> bool:
|
||||||
token = self._token_value.strip()
|
token = self._token_value.strip()
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue