compiler

Log | Files | Refs | README

commit a91290f045a72de724941526ce60db634a05b85e
parent 470f3ab26c412d197a5fde41c8e5e1b99cd4c781
Author: AMIT DUTTA <amitdutta4255@gmail.com>
Date:   Sat, 10 Jan 2026 21:07:14 +0530

Refactor server to use AIOHTTP for WebSocket handling
Diffstat:
Mbackend.py | 295++++++++++++++++++++++++++++++++++++++++---------------------------------------
1 file changed, 149 insertions(+), 146 deletions(-)

diff --git a/backend.py b/backend.py @@ -1,5 +1,5 @@ import asyncio -import websockets +from aiohttp import web import json import subprocess import os @@ -8,13 +8,12 @@ import platform import threading import re import importlib.util -import http # ================================================================================== -# CLOUD COMPILER SERVER (Python, C, C++) - RENDER COMPATIBLE +# CLOUD COMPILER SERVER (AIOHTTP) - RENDER COMPATIBLE # ================================================================================== -def install_package(package_name, websocket=None, loop=None): +def install_package(package_name, ws=None, loop=None): """Attempt to install a python package via pip.""" try: if importlib.util.find_spec(package_name) is not None: @@ -22,9 +21,9 @@ def install_package(package_name, websocket=None, loop=None): msg = f"Installing missing package: {package_name}..." print(msg) - if websocket and loop: + if ws and loop: asyncio.run_coroutine_threadsafe( - websocket.send(json.dumps({'type': 'status', 'msg': msg})), + ws.send_json({'type': 'status', 'msg': msg}), loop ) @@ -32,13 +31,13 @@ def install_package(package_name, websocket=None, loop=None): except Exception as e: error_msg = f"Failed to install {package_name}: {e}" print(error_msg) - if websocket and loop: + if ws and loop: asyncio.run_coroutine_threadsafe( - websocket.send(json.dumps({'type': 'stdout', 'data': f"\n[Error] {error_msg}\n"})), + ws.send_json({'type': 'stdout', 'data': f"\n[Error] {error_msg}\n"}), loop ) -def check_and_install_packages(code, websocket=None, loop=None): +def check_and_install_packages(code, ws=None, loop=None): """Scan Python code for imports and install them if missing.""" imports = re.findall(r'^\s*import\s+(\w+)', code, re.MULTILINE) from_imports = re.findall(r'^\s*from\s+(\w+)', code, re.MULTILINE) @@ -46,10 +45,19 @@ def check_and_install_packages(code, websocket=None, loop=None): for pkg in unique_packages: if pkg in ['os', 'sys', 'time', 'random', 'math', 'json', 'asyncio', 'threading', 'platform', 'subprocess', 're']: continue - install_package(pkg, websocket, loop) + install_package(pkg, ws, loop) -async def run_code(websocket): - print(f"Client connected: {websocket.remote_address}") +async def handle_client(request): + # --- HEALTH CHECK HANDLING --- + # Render sends HEAD/GET to root. If not a websocket upgrade, return OK. + if request.headers.get("Upgrade", "").lower() != "websocket": + return web.Response(text="OK") + + # --- WEBSOCKET HANDLING --- + ws = web.WebSocketResponse() + await ws.prepare(request) + + print(f"Client connected: {request.remote}") process = None # Helper to read output stream in a separate thread @@ -60,155 +68,150 @@ async def run_code(websocket): char = stream.read(1) if not char: break + # aiohttp's send_json is a coroutine asyncio.run_coroutine_threadsafe( - websocket.send(json.dumps({'type': 'stdout', 'data': char})), + ws.send_json({'type': 'stdout', 'data': char}), loop ) except Exception: pass try: - async for message in websocket: - data = json.loads(message) - - if data.get('type') == 'run': - code = data.get('code') - language = data.get('language', 'python') + async for msg in ws: + if msg.type == web.WSMsgType.TEXT: + data = json.loads(msg.data) - # --- PYTHON HANDLING --- - if language == 'python': - await websocket.send(json.dumps({'type': 'status', 'msg': 'Checking dependencies...'})) - # Run package check in executor to avoid blocking main loop - loop = asyncio.get_running_loop() - await loop.run_in_executor(None, check_and_install_packages, code, websocket, loop) - - filename = "temp_script.py" - with open(filename, "w", encoding="utf-8") as f: - f.write(code) - - env = os.environ.copy() - env["PYTHONIOENCODING"] = "utf-8" - - process = subprocess.Popen( - [sys.executable, "-u", filename], - stdin=subprocess.PIPE, - stdout=subprocess.PIPE, - stderr=subprocess.STDOUT, - text=True, - bufsize=0, - encoding='utf-8', - env=env - ) - await websocket.send(json.dumps({'type': 'status', 'msg': 'Running Python...'})) - - # --- C HANDLING --- - elif language == 'c': - filename = "temp_code.c" - executable = "./a.out" if platform.system() != "Windows" else "a.exe" - - with open(filename, "w", encoding="utf-8") as f: - f.write(code) - - await websocket.send(json.dumps({'type': 'status', 'msg': 'Compiling C...'})) - - compile_process = subprocess.run( - ["gcc", filename, "-o", executable], - capture_output=True, - text=True - ) - - if compile_process.returncode != 0: - await websocket.send(json.dumps({'type': 'stdout', 'data': f"Compilation Error:\n{compile_process.stderr}"})) - continue - - await websocket.send(json.dumps({'type': 'status', 'msg': 'Running C Binary...'})) - - process = subprocess.Popen( - [executable], - stdin=subprocess.PIPE, - stdout=subprocess.PIPE, - stderr=subprocess.STDOUT, - text=True, - bufsize=0, - encoding='utf-8' - ) - - # --- C++ HANDLING --- - elif language == 'cpp': - filename = "temp_code.cpp" - executable = "./a.out" if platform.system() != "Windows" else "a.exe" - - with open(filename, "w", encoding="utf-8") as f: - f.write(code) - - await websocket.send(json.dumps({'type': 'status', 'msg': 'Compiling C++...'})) + if data.get('type') == 'run': + code = data.get('code') + language = data.get('language', 'python') - compile_process = subprocess.run( - ["g++", filename, "-o", executable], - capture_output=True, - text=True - ) - - if compile_process.returncode != 0: - await websocket.send(json.dumps({'type': 'stdout', 'data': f"Compilation Error:\n{compile_process.stderr}"})) - continue - - await websocket.send(json.dumps({'type': 'status', 'msg': 'Running C++ Binary...'})) - - process = subprocess.Popen( - [executable], - stdin=subprocess.PIPE, - stdout=subprocess.PIPE, - stderr=subprocess.STDOUT, - text=True, - bufsize=0, - encoding='utf-8' - ) - - if process: - loop = asyncio.get_running_loop() - thread = threading.Thread(target=read_stream, args=(process.stdout, loop)) - thread.daemon = True - thread.start() - - elif data.get('type') == 'input': - if process and process.poll() is None: - user_input = data.get('data') - try: - process.stdin.write(user_input) - process.stdin.flush() - except Exception: - pass - - except websockets.exceptions.ConnectionClosed: - print("Client disconnected") - except Exception as e: - print(f"Server Error: {e}") + # --- PYTHON HANDLING --- + if language == 'python': + await ws.send_json({'type': 'status', 'msg': 'Checking dependencies...'}) + loop = asyncio.get_running_loop() + await loop.run_in_executor(None, check_and_install_packages, code, ws, loop) + + filename = "temp_script.py" + with open(filename, "w", encoding="utf-8") as f: + f.write(code) + + env = os.environ.copy() + env["PYTHONIOENCODING"] = "utf-8" + + process = subprocess.Popen( + [sys.executable, "-u", filename], + stdin=subprocess.PIPE, + stdout=subprocess.PIPE, + stderr=subprocess.STDOUT, + text=True, + bufsize=0, + encoding='utf-8', + env=env + ) + await ws.send_json({'type': 'status', 'msg': 'Running Python...'}) + + # --- C HANDLING --- + elif language == 'c': + filename = "temp_code.c" + executable = "./a.out" if platform.system() != "Windows" else "a.exe" + + with open(filename, "w", encoding="utf-8") as f: + f.write(code) + + await ws.send_json({'type': 'status', 'msg': 'Compiling C...'}) + + compile_process = subprocess.run( + ["gcc", filename, "-o", executable], + capture_output=True, + text=True + ) + + if compile_process.returncode != 0: + await ws.send_json({'type': 'stdout', 'data': f"Compilation Error:\n{compile_process.stderr}"}) + continue + + await ws.send_json({'type': 'status', 'msg': 'Running C Binary...'}) + + process = subprocess.Popen( + [executable], + stdin=subprocess.PIPE, + stdout=subprocess.PIPE, + stderr=subprocess.STDOUT, + text=True, + bufsize=0, + encoding='utf-8' + ) + + # --- C++ HANDLING --- + elif language == 'cpp': + filename = "temp_code.cpp" + executable = "./a.out" if platform.system() != "Windows" else "a.exe" + + with open(filename, "w", encoding="utf-8") as f: + f.write(code) + + await ws.send_json({'type': 'status', 'msg': 'Compiling C++...'}) + + compile_process = subprocess.run( + ["g++", filename, "-o", executable], + capture_output=True, + text=True + ) + + if compile_process.returncode != 0: + await ws.send_json({'type': 'stdout', 'data': f"Compilation Error:\n{compile_process.stderr}"}) + continue + + await ws.send_json({'type': 'status', 'msg': 'Running C++ Binary...'}) + + process = subprocess.Popen( + [executable], + stdin=subprocess.PIPE, + stdout=subprocess.PIPE, + stderr=subprocess.STDOUT, + text=True, + bufsize=0, + encoding='utf-8' + ) + + if process: + loop = asyncio.get_running_loop() + thread = threading.Thread(target=read_stream, args=(process.stdout, loop)) + thread.daemon = True + thread.start() + + elif data.get('type') == 'input': + if process and process.poll() is None: + user_input = data.get('data') + try: + process.stdin.write(user_input) + process.stdin.flush() + except Exception: + pass + + elif msg.type == web.WSMsgType.ERROR: + print(f'ws connection closed with exception {ws.exception()}') + finally: if process: process.kill() + print("Client disconnected") -async def health_check(path, request_headers): - """ - Handler for HTTP requests (Health Checks) before WebSocket handshake. - Render uses HEAD/GET on / to check health. - """ - # 1. Check for standard health check paths - if path == "/" or path == "/healthz": - return http.HTTPStatus.OK, [], b"OK" - - # 2. Check headers to see if it's a websocket upgrade attempt - # If it lacks 'Upgrade' or 'Connection' headers, it's likely a health check - if "Upgrade" not in request_headers or "websocket" not in request_headers.get("Upgrade", "").lower(): - return http.HTTPStatus.OK, [], b"OK" - - # 3. Proceed with WebSocket Handshake - return None + return ws async def main(): port = int(os.environ.get("PORT", 8765)) - print(f"Server started on port {port}...") - async with websockets.serve(run_code, "0.0.0.0", port, process_request=health_check): - await asyncio.Future() + app = web.Application() + # Route root path to the unified handler (handles both HTTP checks and WS upgrades) + app.add_routes([web.get('/', handle_client)]) + + runner = web.AppRunner(app) + await runner.setup() + site = web.TCPSite(runner, '0.0.0.0', port) + print(f"AIOHTTP Server started on port {port}...") + await site.start() + + # Keep the server running + await asyncio.Event().wait() if __name__ == "__main__": asyncio.run(main())
© notamitgamer • Site Built: 2026-07-21 13:58:23 UTC • git-mirror commit: 1037f62 [view raw info]
Originally created with stagit • modified by notamitgamer
Forked from github.com/notamitgamer/git-mirror