# -*- coding: utf-8 -*- import socket import threading import time import sys import inspect PORT_FOR_A = 8080 # Port for device A to connect PORT_FOR_BROWSER = 8081 # Port for browser to connect a_device_conn = None a_device_lock = threading.Lock() a_device_connected = threading.Event() def log_info(source, message): """Log info message""" timestamp = time.strftime("%Y-%m-%d %H:%M:%S") frame = inspect.currentframe().f_back line = frame.f_lineno print("[%s] [INFO] [%s] (line %d) %s" % (timestamp, source, line, message)) def log_error(source, message): """Log error message""" timestamp = time.strftime("%Y-%m-%d %H:%M:%S") frame = inspect.currentframe().f_back line = frame.f_lineno print("[%s] [ERROR] [%s] (line %d) %s" % (timestamp, source, line, message)) def handle_browser(browser_conn, browser_addr): """Handle browser connection""" global a_device_conn log_info("Browser", "Connected from: %s:%d" % browser_addr) # Wait for device A to connect if not a_device_connected.wait(timeout=5): error_msg = b"HTTP/1.1 503 Service Unavailable\r\nContent-Type: text/plain\r\n\r\nDevice A not connected\r\n" browser_conn.send(error_msg) browser_conn.close() log_info("Browser", "Disconnected (A not connected): %s:%d" % browser_addr) return # Read HTTP request request_start = time.time() request_data = b"" browser_conn.settimeout(30) try: while b"\r\n\r\n" not in request_data: chunk = browser_conn.recv(8192) if not chunk: browser_conn.close() log_info("Browser", "Disconnected (no request): %s:%d" % browser_addr) return request_data += chunk except socket.timeout: browser_conn.close() log_info("Browser", "Disconnected (timeout): %s:%d" % browser_addr) return # Parse HTTP request request_lines = request_data.split(b"\r\n") method = "" url = "" if request_lines: first_line = request_lines[0].decode('utf-8', errors='ignore') parts = first_line.split() if len(parts) >= 2: method = parts[0] url = parts[1] body_size = len(request_data) log_info("Browser", "%s:%d - %s %s - Body: %d bytes" % (browser_addr[0], browser_addr[1], method, url, body_size)) # Get device A connection with a_device_lock: device_a = a_device_conn if not device_a: browser_conn.close() log_error("Browser", "Disconnected (A closed): %s:%d" % browser_addr) return # Send request to device A device_a.sendall(request_data) # Wait for response response_data = b"" device_a.settimeout(30) try: while True: chunk = device_a.recv(8192) if not chunk: break response_data += chunk if b"\r\n\r\n" in response_data: if method == "CONNECT" and b"200 Connection Established" in response_data: break if len(response_data) > 8192: break except: pass # Parse response status response_status = "Unknown" if response_data: response_lines = response_data.split(b"\r\n") if response_lines: status_line = response_lines[0].decode('utf-8', errors='ignore') status_parts = status_line.split() if len(status_parts) >= 2: response_status = status_parts[1] # Send response to browser browser_conn.sendall(response_data) # Calculate response time response_time = int((time.time() - request_start) * 1000) log_info("Browser", "%s:%d - %s %s - Status: %s - Time: %dms" % (browser_addr[0], browser_addr[1], method, url, response_status, response_time)) # For CONNECT, establish tunnel if method == "CONNECT" and response_status == "200": log_info("System", "Tunnel established between browser and Device A") def forward(src, dst): try: while True: data = src.recv(8192) if not data: break dst.sendall(data) except: pass t1 = threading.Thread(target=forward, args=(browser_conn, device_a)) t2 = threading.Thread(target=forward, args=(device_a, browser_conn)) t1.start() t2.start() t1.join() t2.join() browser_conn.close() log_info("Browser", "Disconnected: %s:%d" % browser_addr) 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 # Send connection confirmation conn.send(b"PROXY_CONNECTED\r\n") a_device_connected.set() log_info("DeviceA", "Connected: %s:%d" % addr) # Keep connection open by waiting on an event (never triggered) # Connection will be closed when browser tunnel ends or error occurs 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 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', PORT_FOR_BROWSER)) server.listen(10) log_info("System", "Browser port started on %d" % PORT_FOR_BROWSER) while True: conn, addr = server.accept() t = threading.Thread(target=handle_browser, args=(conn, addr)) t.start() def run_a_device_server(): """Start device A connection 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', PORT_FOR_A)) server.listen(1) log_info("System", "Device A port started on %d" % PORT_FOR_A) while True: conn, addr = server.accept() t = threading.Thread(target=handle_a_device, args=(conn, addr)) t.start() def main(): print("=" * 60) print(" Proxy Server (Run on Computer B)") print("=" * 60) print("Device A port:", PORT_FOR_A) print("Browser port:", PORT_FOR_BROWSER) print("=" * 60) print("Waiting for connections...\n") t1 = threading.Thread(target=run_a_device_server) t2 = threading.Thread(target=run_browser_server) t1.daemon = True t2.daemon = True t1.start() t2.start() try: while True: time.sleep(1) except KeyboardInterrupt: log_info("System", "Received Ctrl+C, exiting...") sys.exit(0) if __name__ == '__main__': main()