#!/usr/bin/env python3 """ Simple HTTP server for WebSocket client testing with password protection. Usage: python3 start_web_server.py [--port PORT] [--password PASSWORD] Open in browser: http://localhost:PORT/client.html """ import argparse import base64 import http.server import socketserver import os import sys import threading from pathlib import Path PORT = 8080 SCRIPT_DIR = Path(__file__).parent.resolve() # Default password DEFAULT_PASSWORD = "admin" class AuthHandler(http.server.SimpleHTTPRequestHandler): """HTTP handler with Basic Auth.""" def __init__(self, *args, password="admin", **kwargs): self.password = password super().__init__(*args, **kwargs) def check_auth(self): """Check Basic Auth credentials.""" auth_header = self.headers.get('Authorization') if not auth_header: return False try: # Decode "Basic base64(username:password)" auth_type, auth_string = auth_header.split(' ', 1) if auth_type.lower() != 'basic': return False decoded = base64.b64decode(auth_string).decode('utf-8') username, password = decoded.split(':', 1) # For now, we only check password (username is ignored) return password == self.password except Exception: return False def send_auth_request(self): """Send 401 Unauthorized response.""" self.send_response(401) self.send_header('WWW-Authenticate', 'Basic realm="Wyoming Satellite"') self.send_header('Content-type', 'text/html; charset=utf-8') self.end_headers() self.wfile.write(b''' 401 Unauthorized

šŸ” Wyoming Satellite — Authentication Required

Please enter the password to access the Web UI.

''') def do_GET(self): """Handle GET requests with authentication.""" # Skip auth for static files that are already protected if not self.check_auth(): self.send_auth_request() return super().do_GET() print(f"[WebServer] āœ… {self.requestline} → authenticated") def log_message(self, format, *args): # Suppress default logging pass def create_auth_handler(password): """Create auth handler with password.""" class Handler(AuthHandler): def __init__(self, *args, **kwargs): super().__init__(*args, password=password, **kwargs) return Handler class ReusableTCPServer(socketserver.TCPServer): """TCP Server with address reuse enabled.""" allow_reuse_address = True def main(): parser = argparse.ArgumentParser(description='WebSocket Client Web Server with Password Protection') parser.add_argument('--port', type=int, default=PORT, help=f'Port (default: {PORT})') parser.add_argument('--host', default='0.0.0.0', help='Host (default: 0.0.0.0)') parser.add_argument('--password', default=DEFAULT_PASSWORD, help=f'Password (default: {DEFAULT_PASSWORD})') args = parser.parse_args() # Change to script directory os.chdir(SCRIPT_DIR) # Create handler with password Handler = create_auth_handler(args.password) print(f""" ╔══════════════════════════════════════════════════════════╗ ā•‘ šŸ›°ļø Wyoming Satellite WebSocket Client Server ā•‘ ╠══════════════════════════════════════════════════════════╣ ā•‘ Serving: {str(SCRIPT_DIR):<45} ā•‘ ā•‘ URL: http://localhost:{args.port:<36} ā•‘ ā•‘ Remote: http://:{args.port:<32} ā•‘ ā•‘ Password: {'*' * len(args.password):<45} ā•‘ ā•‘ ā•‘ ā•‘ Press Ctrl+C to stop ā•‘ ā•šā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā• """) try: with ReusableTCPServer((args.host, args.port), Handler) as httpd: print(f"šŸš€ Server started on http://{args.host}:{args.port}") print("šŸ“– Serving files from:", SCRIPT_DIR) print(f"šŸ” Password protected ({len(args.password)} characters)") print("\nāŒØļø Press Ctrl+C to stop\n") try: httpd.serve_forever() except KeyboardInterrupt: print("\nšŸ‘‹ Shutting down gracefully...") # Shutdown in a separate thread to avoid deadlock shutdown_thread = threading.Thread(target=httpd.shutdown) shutdown_thread.start() shutdown_thread.join(timeout=2) httpd.server_close() print("āœ… Server stopped successfully") except OSError as e: print(f"\nāŒ Error: Could not bind to port {args.port}") print(f" {e}") print("\nšŸ’” Try using a different port:") print(f" python3 {sys.argv[0]} --port 8081\n") sys.exit(1) except Exception as e: print(f"\nāŒ Unexpected error: {e}") sys.exit(1) if __name__ == "__main__": main()