MiniABS 1.1: HTTPS support

- Bundle current root certificates (Let's Encrypt, GTS, Sectigo, DigiCert G2,
  Amazon, ...) missing from Android 2.x's store (Roots.java, tools/gen_roots.py)
- Ask once for unknown certificates, showing the SHA-256 fingerprint, and
  remember the answer; replaces "accept any certificate" (Trust*, Settings)
- Local streaming proxy so Android 2.x's MediaPlayer can play https:// servers,
  with seeking and a fresh token per request (StreamProxy.java)
- Tests over real TLS with good, expired, wrong-host, forged and self-signed
  certificates (test/make_pki.sh, TrustTest.java)
- README: server setup for a legacy TLS 1.0 port

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
xockoandClaude Opus 5.5 committed 2026-10-14 20:07:55 +02:00
1 parent 4fe9eed870
commit b7e43d7d61
24 files changed
+1326 -88

No files matched your search

+34 -1
View File
@@ -14,6 +14,8 @@ from http.server import BaseHTTPRequestHandler, ThreadingHTTPServer
PORT = int(sys.argv[1])
LEGACY = len(sys.argv) > 2 and sys.argv[2] == "legacy"
CHAIN = sys.argv[3] if len(sys.argv) > 3 else None # serve HTTPS with this certificate chain
AUDIO = bytes(i % 251 for i in range(200000))
lock = threading.Lock()
state = {"access": None, "refresh": None, "n": 0, "log": [], "progress": {}, "refreshes": 0}
@@ -159,6 +161,26 @@ class H(BaseHTTPRequestHandler):
"currentTime": 600.0, "duration": 1807.5, "playMethod": 0,
"audioTracks": [{"index": 1, "contentUrl": "/api/items/pod1/file/ino7",
"mimeType": "audio/mpeg", "duration": 1807.5}]})
if path == "/api/items/pod1/file/ino7":
rng = self.headers.get("Range")
if rng and rng.startswith("bytes="):
a, _, b = rng[6:].partition("-")
a = int(a)
b = int(b) if b else len(AUDIO) - 1
part = AUDIO[a:b + 1]
self.send_response(206)
self.send_header("Content-Range", "bytes %d-%d/%d" % (a, a + len(part) - 1, len(AUDIO)))
else:
part = AUDIO
self.send_response(200)
self.send_header("Content-Type", "audio/mpeg")
self.send_header("Accept-Ranges", "bytes")
self.send_header("Content-Length", str(len(part)))
self.send_header("Connection", "close")
self.end_headers()
if method != "HEAD":
self.wfile.write(part)
return
if path.startswith("/api/session/") and method == "POST":
return self.send(200, "OK", "text/plain")
if path.startswith("/api/me/progress/") and method == "PATCH":
@@ -172,8 +194,19 @@ class H(BaseHTTPRequestHandler):
def do_POST(self):
self.handle_any("POST")
def do_HEAD(self):
self.handle_any("HEAD")
def do_PATCH(self):
self.handle_any("PATCH")
ThreadingHTTPServer(("127.0.0.1", PORT), H).serve_forever()
srv = ThreadingHTTPServer(("127.0.0.1", PORT), H)
if CHAIN:
import ssl
ctx = ssl.SSLContext(ssl.PROTOCOL_TLS_SERVER)
ctx.load_cert_chain(CHAIN, CHAIN + ".key")
srv.socket = ctx.wrap_socket(srv.socket, server_side=True)
# a TLS handshake the client rejects shows up as an error here; that's expected in tests
srv.handle_error = lambda request, client_address: None
srv.serve_forever()