jenkins 9204773e7d av-test: publish bounded continuity watch and recent observations
Vendor tested Lesavka observer assets and numeric receipt schema from 6b97ceb1. Verified 65 JS tests, 10 service tests, kustomize render and Kubernetes client dry-run. No media upload, capture activation or changes to the homepage and private OCR.
2026-10-22 21:47:13 -05:00

117 lines
4.4 KiB
Python

#!/usr/bin/env python3
"""Loopback-only aggregate receipt service behind the site's exact-path proxy."""
from http.server import BaseHTTPRequestHandler, HTTPServer
from socketserver import ThreadingMixIn
import json
import os
import socket
import threading
from receipts import ReceiptStore, Refused
from validation import no_duplicate_fields
MAX_BODY = 4096
class Handler(BaseHTTPRequestHandler):
def log_message(self, *_args):
pass # Never log request paths, headers, tokens, bodies or errors.
def reply(self, status, data=None):
body = json.dumps(data, allow_nan=False).encode() if data is not None else b""
self.send_response(status)
self.send_header("Content-Type", "application/json")
self.send_header("Cache-Control", "no-store")
self.send_header("Content-Length", str(len(body)))
self.end_headers()
if body:
self.wfile.write(body)
def do_GET(self):
self.reply(405)
do_HEAD = do_GET
do_OPTIONS = do_GET
def do_POST(self):
try:
if self.path not in ("/av-test/api/start", "/av-test/api/report"):
return self.reply(404)
if any(len(self.headers.get_all(name, [])) > 1 for name in ("Origin", "Authorization", "Content-Type")):
return self.reply(400)
if self.headers.get("Origin") != self.server.origin:
return self.reply(403)
if self.headers.get("Content-Type") != "application/json" or self.headers.get("Transfer-Encoding"):
return self.reply(415)
sizes = self.headers.get_all("Content-Length", [])
if len(sizes) != 1 or not sizes[0].isdigit() or not 0 < int(sizes[0]) <= MAX_BODY:
return self.reply(413)
raw = self.rfile.read(int(sizes[0]))
if len(raw) != int(sizes[0]):
return self.reply(400)
data = json.loads(raw, object_pairs_hook=no_duplicate_fields)
if self.path == "/av-test/api/start":
if data != {}:
return self.reply(400)
return self.reply(201, self.server.store.start())
authorization = self.headers.get("Authorization", "")
token = authorization[7:] if authorization.startswith("Bearer ") else ""
self.server.store.report(data, token)
self.reply(204)
except Refused as error:
self.reply(error.status)
except (ValueError, UnicodeError, RecursionError, TypeError):
self.reply(400)
except (OSError, TimeoutError):
pass
class Server(ThreadingMixIn, HTTPServer):
daemon_threads = True
def __init__(self, address, origin, store, request_timeout=3):
self.origin, self.store = origin, store
self.request_timeout = request_timeout
self.workers = threading.BoundedSemaphore(4)
super().__init__(address, Handler)
def get_request(self):
connection, address = super().get_request()
connection.settimeout(3)
return connection, address
def process_request(self, request, client_address):
if not self.workers.acquire(blocking=False):
self.shutdown_request(request)
return
try:
super().process_request(request, client_address)
except Exception:
self.workers.release()
raise
def process_request_thread(self, request, client_address):
def expire():
try:
request.shutdown(socket.SHUT_RDWR)
except OSError:
pass # The request may already have completed.
# Socket timeouts alone reset after every byte. Bound the entire
# header/body read so a trickling peer cannot occupy a worker forever.
deadline = threading.Timer(self.request_timeout, expire)
deadline.daemon = True
deadline.start()
try:
super().process_request_thread(request, client_address)
finally:
deadline.cancel()
self.workers.release()
def handle_error(self, *_args):
pass # No raw exception/request logging, including unexpected input.
if __name__ == "__main__":
store = ReceiptStore(lambda entry: print(json.dumps(entry, allow_nan=False), flush=True))
Server(("127.0.0.1", 8081), os.environ.get("OBSERVER_ORIGIN", "https://bstein.dev"), store).serve_forever()