#!/usr/bin/env python3 """ HTTP health check server """ import sys import json import threading from http.server import HTTPServer, BaseHTTPRequestHandler from datetime import datetime from logger import log from config import config class SilentHTTPServer(HTTPServer): """HTTP Server that ignores connection reset errors from scanners""" def handle_error(self, request, client_address): exc_type = sys.exc_info()[0] if exc_type in (ConnectionResetError, BrokenPipeError, ConnectionAbortedError): pass # Silently ignore - these are just scanners/health checks disconnecting else: log(f"Health server error from {client_address[0]}: {sys.exc_info()[1]}", 'WARNING') class HealthHandler(BaseHTTPRequestHandler): """Health check request handler""" worker = None # Will be set by start_health_server() dynamodb_available = False def do_GET(self): if self.path == '/health' or self.path == '/': self.send_response(200) self.send_header('Content-Type', 'application/json') self.end_headers() status = { 'status': 'healthy', 'domains': len(self.worker.queue_urls) if self.worker else 0, 'domain_list': list(self.worker.queue_urls.keys()) if self.worker else [], 'dynamodb': self.dynamodb_available, 'features': { 'bounce_rewriting': True, 'auto_reply': self.dynamodb_available, 'forwarding': self.dynamodb_available, 'blocklist': self.dynamodb_available, 'lmtp': config.lmtp_enabled }, 'timestamp': datetime.utcnow().isoformat() } self.wfile.write(json.dumps(status, indent=2).encode()) elif self.path == '/domains': self.send_response(200) self.send_header('Content-Type', 'application/json') self.end_headers() domain_list = list(self.worker.queue_urls.keys()) if self.worker else [] self.wfile.write(json.dumps(domain_list).encode()) else: self.send_response(404) self.end_headers() def log_message(self, format, *args): pass # Suppress HTTP access logs def start_health_server(worker, dynamodb_available: bool): """ Start HTTP health check server Args: worker: UnifiedWorker instance dynamodb_available: Whether DynamoDB is available """ # Set class attributes for handler HealthHandler.worker = worker HealthHandler.dynamodb_available = dynamodb_available server = SilentHTTPServer(('0.0.0.0', config.health_port), HealthHandler) thread = threading.Thread(target=server.serve_forever, daemon=True, name='health-server') thread.start() log(f"Health server on port {config.health_port}")