fix: cached GETs bypass the card lock; compact install step lines (v3.6.47)
The install modal's progress bar froze around 70% and jumped to 100% at
the end: not a math bug - the UI's other polls (/api/stk-status,
/api/poll-status, /api/proactive-log, /api/menu, ...) were card-locked in
do_GET even though they only read cached state, queued behind the install
and exhausted the browser's 6-connections-per-host budget, starving
/api/status. _CARD_FREE_GET lists every card-free GET (status, version,
stk-status, poll-status, proactive-log, menu, events, terminal-profile,
pli-*, commands, net-state, mcc-mnc, scp81/bip status|script|log);
/api/cardinfo and /api/esim/* stay locked (they touch the card).
tests/test_fetch_routes.py holds _CARD_LOCK and asserts the cached
endpoints still answer - verified to time out on the pre-fix list.
The install modal is wider (max-w-3xl) and successful step lines are
compact: '✅ Step 3: LOAD (1/240) - 6101 · 274 B / 3 SMS' (no verbose SW
name, B instead of bytes, the SMS count only when >1); failed steps keep
the full detail and the hint.
sw cache simple-v320; tests: 533 Python / 684 Node.
This commit is contained in:
@@ -31,7 +31,7 @@ from osmocom.tlv import BER_TLV_IE
|
||||
from osmocom.utils import rpad
|
||||
|
||||
|
||||
VERSION = '3.6.46'
|
||||
VERSION = '3.6.47'
|
||||
|
||||
MAX_ENVELOPE_SEGMENTS = 5 # max SMS segments for outgoing C-APDU in ENVELOPE
|
||||
|
||||
@@ -4864,6 +4864,20 @@ _TEST_BLOCKED_PATHS = frozenset([
|
||||
'/api/scp81/gen-install', '/api/scp81/log-clear',
|
||||
'/api/bip/control', '/api/bip/log-clear',
|
||||
])
|
||||
# GET endpoints that only read cached state (or the static file tree): served
|
||||
# without _CARD_LOCK so they stay answerable while a long card operation runs
|
||||
# (see do_GET). Card-touching GETs are deliberately absent: /api/cardinfo
|
||||
# (runs cardinfo on the card) and /api/esim/* (ES10 selection + STORE DATA).
|
||||
_CARD_FREE_GET = frozenset([
|
||||
'/api/status', '/api/test/status', '/api/version',
|
||||
'/api/stk-status', '/api/poll-status', '/api/proactive-log',
|
||||
'/api/menu', '/api/events', '/api/terminal-profile',
|
||||
'/api/pli-qualifiers', '/api/pli-dict', '/api/commands',
|
||||
'/api/net-state', '/api/mcc-mnc',
|
||||
'/api/scp81/status', '/api/bip/status', '/api/scp81/script',
|
||||
'/api/scp81/log', '/api/bip/log',
|
||||
])
|
||||
|
||||
_TEST_COMMAND_TYPES = {name.upper(): code for code, name in PROACTIVE_TYPE_NAMES.items()}
|
||||
|
||||
|
||||
@@ -5424,15 +5438,17 @@ class PysimHandler(BaseHTTPRequestHandler):
|
||||
self._send_json({'error': 'test script running - card commands are '
|
||||
'blocked until it finishes'}, 409)
|
||||
return
|
||||
# /api/status and /api/test/status are pure cached state (no card I/O);
|
||||
# keeping them out of the lock lets the UI report 'initializing' while a
|
||||
# long equip (or a test script step) holds the card lock. Result-shaping
|
||||
# masks everything card-derived when the session is not connected.
|
||||
# Static PWA files are card-free too: they must stay answerable while a
|
||||
# long card operation holds the lock (the server is threaded, so this
|
||||
# bypass is what actually serves the modal's progress poll and page
|
||||
# assets during a RAM install).
|
||||
if self.path in ('/api/status', '/api/test/status') or not self.path.startswith('/api/'):
|
||||
# Pure cached state (no card I/O) is served without the lock: the UI
|
||||
# must be able to report 'initializing' while a long equip (or a test
|
||||
# script step) holds the card lock. The list is deliberately wide:
|
||||
# with a long RAM install the UI polls several of these every few
|
||||
# seconds, and if they queued behind the install they would eat the
|
||||
# browser's per-host connection budget - starving /api/status (the
|
||||
# modal's progress bar froze around 70% and jumped to 100% at the end,
|
||||
# live 2026-09-29). Static PWA files are card-free too. Card-touching
|
||||
# GETs (cardinfo, esim/*) stay locked below.
|
||||
path = self.path.split('?', 1)[0]
|
||||
if path in _CARD_FREE_GET or not path.startswith('/api/'):
|
||||
self._do_GET()
|
||||
return
|
||||
# Serialize all card access: the background STATUS poll runs in its own
|
||||
|
||||
Reference in New Issue
Block a user