email-amazon/email-worker/health_server.py

86 lines
2.9 KiB
Python

#!/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}")