fix: threaded HTTP server so the RAM modal progress is live (v3.6.39)

__main__ built a plain HTTPServer (single-threaded): during a RAM install
POST every other request waited in the accept queue, including the 2 s
/api/status poll that feeds the operation modal - its bar and step
headline never moved and the whole result appeared only at the end (live
2026-09-29).

_build_http_server() now returns a ThreadingHTTPServer (daemon threads).
Card access is unchanged: it is serialized by _CARD_LOCK, not by the
server - every POST is blanket-wrapped in do_POST, every /api/ GET except
/api/status and /api/test/status is wrapped in do_GET, and the background
threads (STATUS poll, _bip_data_available, test runner) take the lock;
the audit found no card-access holes.  do_GET also lets non-/api (static
PWA) paths bypass the lock, so page assets stay answerable during long
operations.

Regression test tests/test_http_concurrency.py: a fast request must
complete while a slow one is in flight (fails on HTTPServer with exactly
the reported symptom: 1.01 s vs 0.20 s); test_startup_cardless patches
the new factory.

sw cache simple-v312; tests: 519 Python / 674 Node.
This commit is contained in:
2026-09-29 01:40:28 +03:00
parent 0027c405c1
commit f942056d21
7 changed files with 91 additions and 9 deletions
+15 -2
View File
@@ -4,7 +4,7 @@ import os
import sys
import time
import traceback
from http.server import HTTPServer
from http.server import ThreadingHTTPServer
from pySim.card_handler import CardHandler
from pySim.commands import SimCardCommands
from pySim.exceptions import NoCardError
@@ -37,6 +37,19 @@ def _default_mcc_mnc_list():
return os.path.join(pkg, 'data', 'mcc-mnc-list.json')
def _build_http_server(host, port, handler):
"""HTTP server for the PWA + API.
Threaded on purpose: card access is serialized by ``_CARD_LOCK`` (all
POSTs, the card-touching GETs and the background threads take it), so
while a long operation holds the lock the cached endpoints and static
files must still be answerable - a single-threaded server starved the
RAM modal's live progress poll (`/api/status.ram_progress`) and showed
the whole install result only at the end. Daemon threads keep a stuck
request from blocking shutdown."""
return ThreadingHTTPServer((host, port), handler)
def main():
global _server_start
_server_start = time.time()
@@ -247,7 +260,7 @@ def main():
orig_onchange(param_name, old, new)
_reattach_tracer()
app._onchange_apdu_trace = _onchange_apdu_trace
server = HTTPServer((opts.http_host, opts.http_port), PysimHandler)
server = _build_http_server(opts.http_host, opts.http_port, PysimHandler)
server.sl = sl
server.scc = scc
server.card = card
+6 -2
View File
@@ -31,7 +31,7 @@ from osmocom.tlv import BER_TLV_IE
from osmocom.utils import rpad
VERSION = '3.6.38'
VERSION = '3.6.39'
MAX_ENVELOPE_SEGMENTS = 5 # max SMS segments for outgoing C-APDU in ENVELOPE
@@ -5328,7 +5328,11 @@ class PysimHandler(BaseHTTPRequestHandler):
# 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.
if self.path in ('/api/status', '/api/test/status'):
# 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/'):
self._do_GET()
return
# Serialize all card access: the background STATUS poll runs in its own