86 lines
2.9 KiB
Python
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}")
|