# -*- coding: utf-8 -*- import socket import threading import time import inspect import sys # Configuration A_PORT = 8080 BROWSER_PORT = 8081 # Global state a_device_conn = None a_device_lock = threading.Lock() a_device_connected = threading.Event() def log_info(source, message): """Log info message with line number""" line_number = inspect.currentframe().f_back.f_lineno print("[%s] [INFO] [%s] (line %d) %s" % ( time.strftime("%Y-%m-%d %H:%M:%S"), source, line_number, message )) def log_error(source, message): """Log error message with line number""" line_number = inspect.currentframe().f_back.f_lineno print("[%s] [ERROR] [%s] (line %d) %s" % ( time.strftime("%Y-%m-%d %H:%M:%S"), source, line_number, message )) def handle_a_device(conn, addr): """Handle device A connection - just store and hold""" global a_device_conn with a_device_lock: if a_device_conn: conn.send(b"ERROR: Another A device is already connected\r\n") conn.close() log_info("DeviceA", "Rejected connection from %s:%d" % addr) return a_device_conn = conn conn.send(b"PROXY_CONNECTED\r\n") response = conn.recv(1024) if response and b"READY" in response: a_device_connected.set() log_info("DeviceA", "Connected: %s:%d" % addr) else: with a_device_lock: a_device_conn = None conn.close() log_info("DeviceA", "Failed to receive READY from %s:%d" % addr) return try: event = threading.Event() event.wait() except: pass finally: with a_device_lock: if a_device_conn == conn: a_device_conn = None a_device_connected.clear() conn.close() log_info("DeviceA", "Disconnected") def handle_browser_request(browser_conn, browser_addr): """Handle a request from browser""" try: browser_conn.settimeout(30) data = b"" while True: chunk = browser_conn.recv(8192) data += chunk if b"\r\n\r\n" in data: headers_end = data.find(b"\r\n\r\n") headers = data[:headers_end].decode('utf-8', errors='ignore') content_length = 0 for line in headers.split("\r\n"): if line.lower().startswith("content-length:"): content_length = int(line.split(":")[1].strip()) break body_start = headers_end + 4 if len(data) - body_start >= content_length: break elif chunk == b"": break if not data: return request_lines = data.split(b"\r\n") if request_lines: first_line = request_lines[0].decode('utf-8', errors='ignore') parts = first_line.split() if len(parts) >= 2: method = parts[0] path = parts[1] body_size = len(data) - data.find(b"\r\n\r\n") - 4 if b"\r\n\r\n" in data else 0 log_info("Browser", "Request: %s %s - Body: %d bytes" % (method, path, body_size)) with a_device_lock: if not a_device_conn: error_response = b"HTTP/1.1 503 Service Unavailable\r\nContent-Type: text/plain\r\n\r\nDevice A not connected\r\n" browser_conn.sendall(error_response) log_error("Browser", "Device A not connected for request") return a_device_conn.sendall(data) response = b"" while True: chunk = a_device_conn.recv(8192) if not chunk: break response += chunk if b"\r\n\r\n" in response: headers_end = response.find(b"\r\n\r\n") headers = response[:headers_end].decode('utf-8', errors='ignore') status_code = "500" for line in headers.split("\r\n"): if line.startswith("HTTP/"): status_code = line.split()[1] break content_length = 0 for line in headers.split("\r\n"): if line.lower().startswith("content-length:"): content_length = int(line.split(":")[1].strip()) break body_start = headers_end + 4 if len(response) - body_start >= content_length: break browser_conn.sendall(response) log_info("Browser", "Response: %s %s - Status: %s" % (method, path, status_code)) except socket.timeout: log_error("Browser", "Request timeout from %s:%d" % browser_addr) error_response = b"HTTP/1.1 504 Gateway Timeout\r\nContent-Type: text/plain\r\n\r\nRequest timeout\r\n" browser_conn.sendall(error_response) except Exception as e: log_error("Browser", "Error handling request: %s" % str(e)) finally: browser_conn.close() def handle_browser_client(conn, addr): """Handle browser client connection""" log_info("Browser", "Connected from: %s:%d" % addr) handle_browser_request(conn, addr) log_info("Browser", "Disconnected: %s:%d" % addr) def run_a_server(): """Start device A listening port""" server = socket.socket(socket.AF_INET, socket.SOCK_STREAM) server.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) server.bind(('0.0.0.0', A_PORT)) server.listen(1) log_info("System", "Device A port started on %d" % A_PORT) while True: try: conn, addr = server.accept() t = threading.Thread(target=handle_a_device, args=(conn, addr)) t.daemon = True t.start() except: break server.close() def run_browser_server(): """Start browser listening port""" server = socket.socket(socket.AF_INET, socket.SOCK_STREAM) server.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) server.bind(('0.0.0.0', BROWSER_PORT)) server.listen(5) log_info("System", "Browser port started on %d" % BROWSER_PORT) while True: try: conn, addr = server.accept() t = threading.Thread(target=handle_browser_client, args=(conn, addr)) t.daemon = True t.start() except: break server.close() def main(): print("=" * 60) print(" Proxy Server (Run on Computer B)") print("=" * 60) print("Device A port:", A_PORT) print("Browser port:", BROWSER_PORT) print("=" * 60) print("Waiting for connections...") print() t1 = threading.Thread(target=run_a_server) t1.daemon = True t1.start() t2 = threading.Thread(target=run_browser_server) t2.daemon = True t2.start() try: while True: time.sleep(1) except KeyboardInterrupt: print("\nShutting down...") sys.exit(0) if __name__ == "__main__": main()