diff --git a/proxy/proxy_client.py b/proxy/proxy_client.py index b9726d1..1a7e3b9 100644 --- a/proxy/proxy_client.py +++ b/proxy/proxy_client.py @@ -98,9 +98,14 @@ def modify_request_headers(request_data): def handle_request(request_data): """Handle a single request from device B - completely independent thread""" - request_id = extract_request_id(request_data) + if isinstance(request_data, bytes): + request_data_str = request_data.decode('utf-8') + else: + request_data_str = request_data - modified_data, method, path = modify_request_headers(request_data) + request_id = extract_request_id(request_data_str) + + modified_data, method, path = modify_request_headers(request_data_str) body_size = 0 if "\r\n\r\n" in modified_data: @@ -115,18 +120,20 @@ def handle_request(request_data): target_sock.connect((TARGET_HOST, TARGET_PORT)) log_info("System", "Connected to target: %s:%d" % (TARGET_HOST, TARGET_PORT)) + if isinstance(modified_data, str): + modified_data = modified_data.encode('utf-8') target_sock.sendall(modified_data) - response = "" + response = b"" while True: chunk = target_sock.recv(8192) if not chunk: break response += chunk - if "\r\n\r\n" in response: - headers_end = response.find("\r\n\r\n") - headers = response[:headers_end] + if b"\r\n\r\n" in response: + headers_end = response.find(b"\r\n\r\n") + headers = response[:headers_end].decode('utf-8') content_length = 0 for line in headers.split("\r\n"): @@ -139,16 +146,16 @@ def handle_request(request_data): break if request_id: - if "\r\n\r\n" in response: - headers_end = response.find("\r\n\r\n") - status_line = response[:headers_end].split("\r\n")[0] - rest = response[headers_end:] - response = status_line + "\r\nX-Proxy-Request-ID: " + request_id + rest + if b"\r\n\r\n" in response: + headers_end = response.find(b"\r\n\r\n") + headers = response[:headers_end] + body = response[headers_end:] + response = headers + ("\r\nX-Proxy-Request-ID: " + request_id).encode('utf-8') + body status_code = "500" - if "\r\n\r\n" in response: - headers_end = response.find("\r\n\r\n") - headers = response[:headers_end] + if b"\r\n\r\n" in response: + headers_end = response.find(b"\r\n\r\n") + headers = response[:headers_end].decode('utf-8') for line in headers.split("\r\n"): if line.startswith("HTTP/"): status_code = line.split()[1] @@ -161,19 +168,19 @@ def handle_request(request_data): except socket.error as e: if e.errno == 111 or e.errno == 10061: log_error("System", "Cannot connect to target: %s:%d" % (TARGET_HOST, TARGET_PORT)) - error_response = "HTTP/1.1 502 Bad Gateway\r\nX-Proxy-Request-ID: " + (request_id if request_id else "") + "\r\nContent-Type: text/plain\r\n\r\nCannot connect to target: %s:%d\r\n" % (TARGET_HOST, TARGET_PORT) + error_response = ("HTTP/1.1 502 Bad Gateway\r\nX-Proxy-Request-ID: " + (request_id if request_id else "") + "\r\nContent-Type: text/plain\r\n\r\nCannot connect to target: %s:%d\r\n" % (TARGET_HOST, TARGET_PORT)).encode('utf-8') return error_response else: log_error("System", "Socket error: %s" % str(e)) - error_response = "HTTP/1.1 502 Bad Gateway\r\nX-Proxy-Request-ID: " + (request_id if request_id else "") + "\r\nContent-Type: text/plain\r\n\r\nSocket error\r\n" + error_response = ("HTTP/1.1 502 Bad Gateway\r\nX-Proxy-Request-ID: " + (request_id if request_id else "") + "\r\nContent-Type: text/plain\r\n\r\nSocket error\r\n").encode('utf-8') return error_response except socket.timeout: log_error("System", "Target connection timeout") - error_response = "HTTP/1.1 504 Gateway Timeout\r\nX-Proxy-Request-ID: " + (request_id if request_id else "") + "\r\nContent-Type: text/plain\r\n\r\nConnection timeout\r\n" + error_response = ("HTTP/1.1 504 Gateway Timeout\r\nX-Proxy-Request-ID: " + (request_id if request_id else "") + "\r\nContent-Type: text/plain\r\n\r\nConnection timeout\r\n").encode('utf-8') return error_response except Exception as e: log_error("System", "Error handling request: %s" % str(e)) - error_response = "HTTP/1.1 500 Internal Server Error\r\nX-Proxy-Request-ID: " + (request_id if request_id else "") + "\r\nContent-Type: text/plain\r\n\r\nInternal error\r\n" + error_response = ("HTTP/1.1 500 Internal Server Error\r\nX-Proxy-Request-ID: " + (request_id if request_id else "") + "\r\nContent-Type: text/plain\r\n\r\nInternal error\r\n").encode('utf-8') return error_response finally: target_sock.close() @@ -194,16 +201,16 @@ def listen_for_requests(sock): while True: try: - data = "" + data = b"" while True: chunk = sock.recv(8192) if not chunk: log_info("System", "Connection closed by device B") return data += chunk - if "\r\n\r\n" in data: - headers_end = data.find("\r\n\r\n") - headers = data[:headers_end] + if b"\r\n\r\n" in data: + headers_end = data.find(b"\r\n\r\n") + headers = data[:headers_end].decode('utf-8') content_length = 0 for line in headers.split("\r\n"): @@ -220,8 +227,8 @@ def listen_for_requests(sock): t.start() except socket.timeout: - log_info("System", "Connection idle timeout") - return + log_info("System", "Connection idle timeout, continuing to wait") + continue except Exception as e: log_error("System", "Connection error: %s" % str(e)) return @@ -239,14 +246,14 @@ def connect_to_b_device(b_host, b_port): response = sock.recv(1024) log_info("System", "Received from B: %r" % response) - if not response or "PROXY_CONNECTED" not in response: + if not response or b"PROXY_CONNECTED" not in response: log_error("System", "Failed to receive connection confirmation") sock.close() time.sleep(3) continue log_info("System", "Sending READY to device B") - sock.sendall("READY\r\n") + sock.sendall(b"READY\r\n") log_info("System", "Successfully connected to device B: %s:%d" % (b_host, b_port)) listen_for_requests(sock) diff --git a/proxy/proxy_server.py b/proxy/proxy_server.py index f6bf8e7..d7bdc67 100644 --- a/proxy/proxy_server.py +++ b/proxy/proxy_server.py @@ -60,7 +60,7 @@ def handle_a_device(conn, addr): with a_device_lock: if a_device_conn: - conn.send("ERROR: Another A device is already connected\r\n") + conn.send(b"ERROR: Another A device is already connected\r\n") conn.close() log_info("DeviceA", "Rejected connection from %s:%d" % addr) return @@ -68,7 +68,7 @@ def handle_a_device(conn, addr): a_device_conn = conn log_info("DeviceA", "Sending PROXY_CONNECTED to %s:%d" % addr) - conn.send("PROXY_CONNECTED\r\n") + conn.send(b"PROXY_CONNECTED\r\n") try: conn.settimeout(30) @@ -76,7 +76,7 @@ def handle_a_device(conn, addr): response = conn.recv(1024) log_info("DeviceA", "Received from %s:%d: %r" % (addr[0], addr[1], response)) - if response and "READY" in response: + if response and b"READY" in response: a_device_connected.set() log_info("DeviceA", "Connected: %s:%d" % addr) else: @@ -120,7 +120,7 @@ def process_response_from_a(): sock = a_device_conn - response = "" + response = b"" while True: chunk = sock.recv(8192) if not chunk: @@ -131,9 +131,9 @@ def process_response_from_a(): return response += chunk - if "\r\n\r\n" in response: - headers_end = response.find("\r\n\r\n") - headers = response[:headers_end] + if b"\r\n\r\n" in response: + headers_end = response.find(b"\r\n\r\n") + headers = response[:headers_end].decode('utf-8') request_id = None for line in headers.split("\r\n"): @@ -172,13 +172,15 @@ def handle_browser_request(browser_conn, browser_addr): try: browser_conn.settimeout(30) - data = "" + data = b"" while True: chunk = browser_conn.recv(8192) + if not chunk: + break data += chunk - if "\r\n\r\n" in data: - headers_end = data.find("\r\n\r\n") - headers = data[:headers_end] + if b"\r\n\r\n" in data: + headers_end = data.find(b"\r\n\r\n") + headers = data[:headers_end].decode('utf-8') content_length = 0 for line in headers.split("\r\n"): @@ -189,13 +191,11 @@ def handle_browser_request(browser_conn, browser_addr): body_start = headers_end + 4 if len(data) - body_start >= content_length: break - elif chunk == "": - break if not data: return - request_lines = data.split("\r\n") + request_lines = data.decode('utf-8').split("\r\n") if request_lines: first_line = request_lines[0] parts = first_line.split() @@ -203,14 +203,14 @@ def handle_browser_request(browser_conn, browser_addr): method = parts[0] path = parts[1] - body_size = len(data) - data.find("\r\n\r\n") - 4 if "\r\n\r\n" in data else 0 + body_size = len(data) - data.find(b"\r\n\r\n") - 4 if b"\r\n\r\n" in data else 0 request_id = get_request_id() log_info("Browser", "Request: %s %s - Body: %d bytes - ID: %s" % (method, path, body_size, request_id)) with a_device_lock: if not a_device_conn: - error_response = "HTTP/1.1 503 Service Unavailable\r\nContent-Type: text/plain\r\n\r\nDevice A not connected\r\n" + 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 %s" % request_id) return @@ -221,7 +221,11 @@ def handle_browser_request(browser_conn, browser_addr): 'timestamp': time.time() } - modified_data = "X-Proxy-Request-ID: " + request_id + "\r\n" + data + if isinstance(data, bytes): + modified_data = ("X-Proxy-Request-ID: " + request_id + "\r\n").encode('utf-8') + data + else: + modified_data = "X-Proxy-Request-ID: " + request_id + "\r\n" + data + modified_data = modified_data.encode('utf-8') with a_device_lock: try: @@ -232,7 +236,7 @@ def handle_browser_request(browser_conn, browser_addr): with request_queue_lock: if request_id in request_queue: del request_queue[request_id] - error_response = "HTTP/1.1 502 Bad Gateway\r\nContent-Type: text/plain\r\n\r\nFailed to forward request to Device A\r\n" + error_response = b"HTTP/1.1 502 Bad Gateway\r\nContent-Type: text/plain\r\n\r\nFailed to forward request to Device A\r\n" browser_conn.sendall(error_response) return @@ -251,6 +255,8 @@ def handle_browser_request(browser_conn, browser_addr): time.sleep(0.1) if response: + if isinstance(response, str): + response = response.encode('utf-8') browser_conn.sendall(response) log_info("Browser", "Response sent for request %s" % request_id) else: @@ -258,12 +264,12 @@ def handle_browser_request(browser_conn, browser_addr): if request_id in request_queue: del request_queue[request_id] log_error("Browser", "Response timeout for request %s" % request_id) - error_response = "HTTP/1.1 504 Gateway Timeout\r\nContent-Type: text/plain\r\n\r\nResponse timeout\r\n" + error_response = b"HTTP/1.1 504 Gateway Timeout\r\nContent-Type: text/plain\r\n\r\nResponse timeout\r\n" browser_conn.sendall(error_response) except socket.timeout: log_error("Browser", "Request timeout from %s:%d" % browser_addr) - error_response = "HTTP/1.1 504 Gateway Timeout\r\nContent-Type: text/plain\r\n\r\nRequest timeout\r\n" + 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)) diff --git a/proxy/test_server.py b/proxy/test_server.py new file mode 100644 index 0000000..d40b967 --- /dev/null +++ b/proxy/test_server.py @@ -0,0 +1,33 @@ +# -*- coding: utf-8 -*- +import socket +import threading + +def handle_client(conn, addr): + try: + data = conn.recv(8192) + if data: + lines = data.split("\r\n") + if lines: + method, path, _ = lines[0].split() + print("[%s:%d] %s %s" % (addr[0], addr[1], method, path)) + + response = "HTTP/1.1 200 OK\r\nContent-Type: text/html\r\nContent-Length: 48\r\n\r\nHello from test server!" + conn.sendall(response) + finally: + conn.close() + +def main(): + server = socket.socket(socket.AF_INET, socket.SOCK_STREAM) + server.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) + server.bind(('127.0.0.1', 8888)) + server.listen(5) + print("Test server running on 127.0.0.1:8888") + + while True: + conn, addr = server.accept() + t = threading.Thread(target=handle_client, args=(conn, addr)) + t.daemon = True + t.start() + +if __name__ == "__main__": + main()